From 3e4768efca88124b3ae418d41da923c428598275 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 4 Aug 2024 13:59:13 -0400 Subject: [PATCH 01/69] Revert "Remove invalid test" This reverts commit d0f4b2b02fc3aee3f08239d9c188ca5a2e8ad482. --- test/pleroma/integration/mastodon_websocket_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index f499f54ad..88f32762d 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -268,6 +268,17 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do end) end + test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do + assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) + + capture_log(fn -> + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + + Process.sleep(30) + end) + end + test "accepts valid token on client-sent event", %{token: token} do assert {:ok, pid} = start_socket() From 8c91fd8785c25e694d9341b17b5182041c575166 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 4 Aug 2024 14:58:16 -0400 Subject: [PATCH 02/69] Fix Mastodon WebSocket authentication Mastodon uses the Sec-Websocket-Protocol header to send the auth token. It is not clear if this is a violation of the RFC, but Mastodon is not the first application in the wild to use this header for authentication purposes. Phoenix does not allow accessing this header, so we work around it temporarily with a minor patch to Phoenix 1.7.14. We will reach out to Phoenix to discuss how to make this use case possible. --- changelog.d/mastodon-websocket.fix | 1 + lib/pleroma/web/endpoint.ex | 1 + lib/pleroma/web/mastodon_api/websocket_handler.ex | 11 ++++++++++- mix.exs | 3 ++- mix.lock | 4 ++-- 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 changelog.d/mastodon-websocket.fix diff --git a/changelog.d/mastodon-websocket.fix b/changelog.d/mastodon-websocket.fix new file mode 100644 index 000000000..2c4fe86ef --- /dev/null +++ b/changelog.d/mastodon-websocket.fix @@ -0,0 +1 @@ +Fix Mastodon WebSocket authentication diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index fef907ace..bab3c9fd0 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.Endpoint do websocket: [ path: "/", compress: false, + connect_info: [:sec_websocket_protocol], error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []}, fullsweep_after: 20 ] diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 730295a4c..3ed1cdd6c 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do # This only prepares the connection and is not in the process yet @impl Phoenix.Socket.Transport def connect(%{params: params} = transport_info) do - with access_token <- Map.get(params, "access_token"), + with access_token <- find_access_token(transport_info), {:ok, user, oauth_token} <- authenticate_request(access_token), {:ok, topic} <- Streamer.get_topic(params["stream"], user, oauth_token, params) do @@ -244,4 +244,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do def handle_error(conn, _reason) do Plug.Conn.send_resp(conn, 404, "Not Found") end + + defp find_access_token(%{ + connect_info: %{sec_websocket_protocol: [token]} + }), + do: token + + defp find_access_token(%{params: %{"access_token" => token}}), do: token + + defp find_access_token(_), do: nil end diff --git a/mix.exs b/mix.exs index 69e52e526..88b558a75 100644 --- a/mix.exs +++ b/mix.exs @@ -132,7 +132,8 @@ defmodule Pleroma.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.7.3"}, + {:phoenix, + git: "https://github.com/feld/phoenix", branch: "v1.7.14-websocket-headers", override: true}, {:phoenix_ecto, "~> 4.4"}, {:ecto_sql, "~> 3.10"}, {:ecto_enum, "~> 1.4"}, diff --git a/mix.lock b/mix.lock index 61ede9e5e..a26ac0e84 100644 --- a/mix.lock +++ b/mix.lock @@ -65,7 +65,7 @@ "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"}, - "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"}, "jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"}, "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, @@ -94,7 +94,7 @@ "open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, - "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, + "phoenix": {:git, "https://github.com/feld/phoenix", "fb6dc76c657422e49600896c64aab4253fceaef6", [branch: "v1.7.14-websocket-headers"]}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"}, "phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"}, From e74e0089bf2943f925cbead14154f8b2fa207963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 16 Sep 2024 17:07:39 +0200 Subject: [PATCH 03/69] Repesct :restrict_unauthenticated for hashtag rss/atom feeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/hashtag-feeds-restricted.add | 1 + lib/pleroma/web/feed/tag_controller.ex | 6 +- test/pleroma/web/feed/tag_controller_test.exs | 56 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 changelog.d/hashtag-feeds-restricted.add diff --git a/changelog.d/hashtag-feeds-restricted.add b/changelog.d/hashtag-feeds-restricted.add new file mode 100644 index 000000000..accac9c9c --- /dev/null +++ b/changelog.d/hashtag-feeds-restricted.add @@ -0,0 +1 @@ +Repesct :restrict_unauthenticated for hashtag rss/atom feeds \ No newline at end of file diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index e60767327..02d639296 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.Feed.TagController do alias Pleroma.Web.Feed.FeedView def feed(conn, params) do - if Config.get!([:instance, :public]) do + if not Config.restrict_unauthenticated_access?(:timelines, :local) do render_feed(conn, params) else render_error(conn, :not_found, "Not found") @@ -18,10 +18,12 @@ defmodule Pleroma.Web.Feed.TagController do end defp render_feed(conn, %{"tag" => raw_tag} = params) do + local_only = Config.restrict_unauthenticated_access?(:timelines, :federated) + {format, tag} = parse_tag(raw_tag) activities = - %{type: ["Create"], tag: tag} + %{type: ["Create"], tag: tag, local_only: local_only} |> Pleroma.Maps.put_if_present(:max_id, params["max_id"]) |> ActivityPub.fetch_public_activities() diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index 7d196b228..662235f31 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -191,4 +191,60 @@ defmodule Pleroma.Web.Feed.TagControllerTest do |> response(404) end end + + describe "restricted for unauthenticated" do + test "returns 404 when local timeline is disabled", %{conn: conn} do + clear_config([:restrict_unauthenticated, :timelines], %{local: true, federated: false}) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(404) + end + + test "returns local posts only when federated timeline is disabled", %{conn: conn} do + clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: true}) + + local_user = insert(:user) + remote_user = insert(:user, local: false) + + local_note = + insert(:note, + user: local_user, + data: %{ + "content" => "local post #PleromaArt", + "summary" => "", + "tag" => ["pleromaart"] + } + ) + + remote_note = + insert(:note, + user: remote_user, + data: %{ + "content" => "remote post #PleromaArt", + "summary" => "", + "tag" => ["pleromaart"] + }, + local: false + ) + + insert(:note_activity, user: local_user, note: local_note) + insert(:note_activity, user: remote_user, note: remote_note, local: false) + + response = + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(200) + + xml = parse(response) + + assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart" + + assert xpath(xml, ~x"//channel/item/title/text()"l) == [ + ~c"local post #PleromaArt" + ] + end + end end From 23e5eed4e0e61ea65bd895bee7d8a137fccf3307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 19 Sep 2024 10:57:50 +0200 Subject: [PATCH 04/69] Include session scopes in TokenView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/token-view-scopes.add | 1 + lib/pleroma/web/twitter_api/views/token_view.ex | 3 ++- test/pleroma/web/twitter_api/controller_test.exs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog.d/token-view-scopes.add diff --git a/changelog.d/token-view-scopes.add b/changelog.d/token-view-scopes.add new file mode 100644 index 000000000..e24fa38e6 --- /dev/null +++ b/changelog.d/token-view-scopes.add @@ -0,0 +1 @@ +Include session scopes in TokenView \ No newline at end of file diff --git a/lib/pleroma/web/twitter_api/views/token_view.ex b/lib/pleroma/web/twitter_api/views/token_view.ex index 2e492c13f..36776ce3b 100644 --- a/lib/pleroma/web/twitter_api/views/token_view.ex +++ b/lib/pleroma/web/twitter_api/views/token_view.ex @@ -15,7 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.TokenView do %{ id: token_entry.id, valid_until: token_entry.valid_until, - app_name: token_entry.app.client_name + app_name: token_entry.app.client_name, + scopes: token_entry.scopes } end end diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs index 495d371d2..0019b51af 100644 --- a/test/pleroma/web/twitter_api/controller_test.exs +++ b/test/pleroma/web/twitter_api/controller_test.exs @@ -69,7 +69,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do |> hd() |> Map.keys() - assert keys -- ["id", "app_name", "valid_until"] == [] + assert keys -- ["id", "app_name", "valid_until", "scopes"] == [] end test "revoke token", %{token: token} do From 7dd3a4d86defff9c0960f7e39481215603ac85b9 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 24 Sep 2024 05:54:25 +0200 Subject: [PATCH 05/69] push: make vapid_config fallback to empty array 2024-09-24T03:53:27.770757+00:00 NightmareMoon pleroma: path=/notice/AmJcSqyeyij4W70K36 [error] Preloading for /notice/AmJcSqyeyij4W70K36 failed. ** (FunctionClauseError) no function clause matching in Keyword.get/3 (elixir 1.15.8) lib/keyword.ex:388: Keyword.get(nil, :public_key, nil) (pleroma 2.7.0-3067-g9b76dbd4-dev-lanodan2) lib/pleroma/web/mastodon_api/views/instance_view.ex:262: Pleroma.Web.MastodonAPI.InstanceView.pleroma_configuration/1 (pleroma 2.7.0-3067-g9b76dbd4-dev-lanodan2) lib/pleroma/web/mastodon_api/views/instance_view.ex:45: Pleroma.Web.MastodonAPI.InstanceView.render/2 (pleroma 2.7.0-3067-g9b76dbd4-dev-lanodan2) lib/pleroma/web/preload/providers/instance.ex:28: Pleroma.Web.Preload.Providers.Instance.build_info_tag/1 (pleroma 2.7.0-3067-g9b76dbd4-dev-lanodan2) lib/pleroma/web/preload/providers/instance.ex:21: Pleroma.Web.Preload.Providers.Instance.generate_terms/1 (pleroma 2.7.0-3067-g9b76dbd4-dev-lanodan2) lib/pleroma/web/preload.ex:13: anonymous fn/3 in Pleroma.Web.Preload.build_tags/2 --- changelog.d/vapid_keyword_fallback.fix | 1 + lib/pleroma/web/push.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/vapid_keyword_fallback.fix diff --git a/changelog.d/vapid_keyword_fallback.fix b/changelog.d/vapid_keyword_fallback.fix new file mode 100644 index 000000000..aa48f8938 --- /dev/null +++ b/changelog.d/vapid_keyword_fallback.fix @@ -0,0 +1 @@ +Make vapid_config return empty array, fixing preloading for instances without push notifications configured \ No newline at end of file diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex index 6d777142e..77f77f88e 100644 --- a/lib/pleroma/web/push.ex +++ b/lib/pleroma/web/push.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.Push do end def vapid_config do - Application.get_env(:web_push_encryption, :vapid_details, nil) + Application.get_env(:web_push_encryption, :vapid_details, []) end def enabled, do: match?([subject: _, public_key: _, private_key: _], vapid_config()) From 382426e0338d7918cd2db7c72ede446a2a8f7f4f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 12:41:06 -0400 Subject: [PATCH 06/69] Remove Object.get_by_id_and_maybe_refetch/2 This was only used for poll refreshing and is not a good approach to the problem. --- lib/pleroma/object.ex | 21 --- .../controllers/poll_controller.ex | 2 +- test/pleroma/object_test.exs | 144 ------------------ 3 files changed, 1 insertion(+), 166 deletions(-) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 748f18e6c..77dfda851 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -99,27 +99,6 @@ defmodule Pleroma.Object do def get_by_id(nil), do: nil def get_by_id(id), do: Repo.get(Object, id) - @spec get_by_id_and_maybe_refetch(integer(), list()) :: Object.t() | nil - def get_by_id_and_maybe_refetch(id, opts \\ []) do - with %Object{updated_at: updated_at} = object <- get_by_id(id) do - if opts[:interval] && - NaiveDateTime.diff(NaiveDateTime.utc_now(), updated_at) > opts[:interval] do - case Fetcher.refetch_object(object) do - {:ok, %Object{} = object} -> - object - - e -> - Logger.error("Couldn't refresh #{object.data["id"]}:\n#{inspect(e)}") - object - end - else - object - end - else - nil -> nil - end - end - def get_by_ap_id(nil), do: nil def get_by_ap_id(ap_id) do diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index a2af8148c..303b995f6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do - with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), + with %Object{} = object <- Object.get_by_id(id), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "show.json", %{object: object, for: user}) diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index 48d4d86eb..b3c528e32 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -6,12 +6,10 @@ defmodule Pleroma.ObjectTest do use Pleroma.DataCase use Oban.Testing, repo: Pleroma.Repo - import ExUnit.CaptureLog import Mox import Pleroma.Factory import Tesla.Mock - alias Pleroma.Activity alias Pleroma.Hashtag alias Pleroma.Object alias Pleroma.Repo @@ -282,148 +280,6 @@ defmodule Pleroma.ObjectTest do end end - describe "get_by_id_and_maybe_refetch" do - setup do - mock(fn - %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_original.json"), - headers: HttpRequestMock.activitypub_object_headers() - } - - env -> - apply(HttpRequestMock, :request, [env]) - end) - - mock_modified = fn resp -> - mock(fn - %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} -> - resp - - env -> - apply(HttpRequestMock, :request, [env]) - end) - end - - on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end) - - [mock_modified: mock_modified] - end - - test "refetches if the time since the last refetch is greater than the interval", %{ - mock_modified: mock_modified - } do - %Object{} = - object = - Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", - fetch: true - ) - - Object.set_cache(object) - - assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - - mock_modified.(%Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), - headers: HttpRequestMock.activitypub_object_headers() - }) - - updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) - object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) - assert updated_object == object_in_cache - assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8 - assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3 - end - - test "returns the old object if refetch fails", %{mock_modified: mock_modified} do - %Object{} = - object = - Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", - fetch: true - ) - - Object.set_cache(object) - - assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - - assert capture_log(fn -> - mock_modified.(%Tesla.Env{status: 404, body: ""}) - - updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) - object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) - assert updated_object == object_in_cache - assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - end) =~ - "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d" - end - - test "does not refetch if the time since the last refetch is greater than the interval", %{ - mock_modified: mock_modified - } do - %Object{} = - object = - Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", - fetch: true - ) - - Object.set_cache(object) - - assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - - mock_modified.(%Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), - headers: HttpRequestMock.activitypub_object_headers() - }) - - updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100) - object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) - assert updated_object == object_in_cache - assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - end - - test "preserves internal fields on refetch", %{mock_modified: mock_modified} do - %Object{} = - object = - Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", - fetch: true - ) - - Object.set_cache(object) - - assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 - assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 - - user = insert(:user) - activity = Activity.get_create_by_object_ap_id(object.data["id"]) - {:ok, activity} = CommonAPI.favorite(activity.id, user) - object = Object.get_by_ap_id(activity.data["object"]) - - assert object.data["like_count"] == 1 - - mock_modified.(%Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), - headers: HttpRequestMock.activitypub_object_headers() - }) - - updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) - object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) - assert updated_object == object_in_cache - assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8 - assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3 - - assert updated_object.data["like_count"] == 1 - end - end - describe ":hashtags association" do test "Hashtag records are created with Object record and updated on its change" do user = insert(:user) From 2380ae6dcc267d7d6ff81a55ae95eed718176563 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 13:38:13 -0400 Subject: [PATCH 07/69] Validate an Oban job is inserted for poll refreshes --- .../web/mastodon_api/controllers/poll_controller_test.exs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs index 7912b1d5f..b2cceec51 100644 --- a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollControllerTest do + use Oban.Testing, repo: Pleroma.Repo use Pleroma.Web.ConnCase, async: true alias Pleroma.Object @@ -27,6 +28,11 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do response = json_response_and_validate_schema(conn, 200) id = to_string(object.id) assert %{"id" => ^id, "expired" => false, "multiple" => false} = response + + assert_enqueued( + worker: Pleroma.Workers.PollWorker, + args: %{"op" => "refresh", "activity_id" => activity.id} + ) end test "does not expose polls for private statuses", %{conn: conn} do From c077a14ce1343f5515fa11938df7d808f23a566c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 13:54:56 -0400 Subject: [PATCH 08/69] Add Oban job to handle poll refreshing and stream out the update --- .../controllers/poll_controller.ex | 4 +++ lib/pleroma/workers/poll_worker.ex | 36 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 303b995f6..0d5a57518 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.Workers.PollWorker alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug @@ -33,6 +34,9 @@ defmodule Pleroma.Web.MastodonAPI.PollController do with %Object{} = object <- Object.get_by_id(id), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), true <- Visibility.visible_for_user?(activity, user) do + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert(unique: [period: 60]) + try_render(conn, "show.json", %{object: object, for: user}) else error when is_nil(error) or error == false -> diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index d263aa1b9..0d2d67326 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -11,14 +11,34 @@ defmodule Pleroma.Workers.PollWorker do alias Pleroma.Activity alias Pleroma.Notification alias Pleroma.Object + alias Pleroma.Object.Fetcher + + @stream_out_impl Pleroma.Config.get( + [__MODULE__, :stream_out], + Pleroma.Web.ActivityPub.ActivityPub + ) @impl true def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do - with %Activity{} = activity <- find_poll_activity(activity_id), + with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)}, {:ok, notifications} <- Notification.create_poll_notifications(activity) do Notification.stream(notifications) else - {:error, :poll_activity_not_found} = e -> {:cancel, e} + {:activity, nil} -> {:cancel, :poll_activity_not_found} + e -> {:error, e} + end + end + + def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do + with {_, %Activity{object: object}} <- + {:activity, Activity.get_by_id_with_object(activity_id)}, + {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do + stream_update(activity_id) + + :ok + else + {:activity, nil} -> {:cancel, :poll_activity_not_found} + {:refetch, _} = e -> {:cancel, e} e -> {:error, e} end end @@ -26,12 +46,6 @@ defmodule Pleroma.Workers.PollWorker do @impl true def timeout(_job), do: :timer.seconds(5) - defp find_poll_activity(activity_id) do - with nil <- Activity.get_by_id(activity_id) do - {:error, :poll_activity_not_found} - end - end - def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <- Object.normalize(activity), @@ -49,4 +63,10 @@ defmodule Pleroma.Workers.PollWorker do end def schedule_poll_end(activity), do: {:error, activity} + + defp stream_update(activity_id) do + Activity.get_by_id(activity_id) + |> Activity.normalize() + |> @stream_out_impl.stream_out() + end end From 4b3f604f9529c9ced23f747cb6f6d82fedfadab0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:02:41 -0400 Subject: [PATCH 09/69] Skip refetching poll results if the object's updated_at is newer than the poll closed timestamp --- lib/pleroma/workers/poll_worker.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 0d2d67326..a61c5eac1 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -32,6 +32,8 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do with {_, %Activity{object: object}} <- {:activity, Activity.get_by_id_with_object(activity_id)}, + {:ok, naive_closed} <- NaiveDateTime.from_iso8601(object.data["closed"]), + {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, naive_closed)}, {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do stream_update(activity_id) @@ -39,6 +41,7 @@ defmodule Pleroma.Workers.PollWorker do else {:activity, nil} -> {:cancel, :poll_activity_not_found} {:refetch, _} = e -> {:cancel, e} + {:closed_compare, _} -> {:cancel, :poll_finalized} e -> {:error, e} end end From 47ce3a4a961bd7496f8105bc957dbf958b77d342 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:17:35 -0400 Subject: [PATCH 10/69] Schedule a final poll refresh before streaming out the notifications --- lib/pleroma/workers/poll_worker.ex | 8 ++++++-- test/pleroma/workers/poll_worker_test.exs | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index a61c5eac1..574daa9ba 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -22,6 +22,10 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)}, {:ok, notifications} <- Notification.create_poll_notifications(activity) do + # Schedule a final refresh + __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id}) + |> Oban.insert() + Notification.stream(notifications) else {:activity, nil} -> {:cancel, :poll_activity_not_found} @@ -32,8 +36,8 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do with {_, %Activity{object: object}} <- {:activity, Activity.get_by_id_with_object(activity_id)}, - {:ok, naive_closed} <- NaiveDateTime.from_iso8601(object.data["closed"]), - {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, naive_closed)}, + {:ok, naive_closed} <- NaiveDateTime.from_iso8601(object.data["closed"]), + {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, naive_closed)}, {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do stream_update(activity_id) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index 749df8aff..e1c67f057 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -44,6 +44,12 @@ defmodule Pleroma.Workers.PollWorkerTest do # Ensure notifications were streamed out when job executes assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_)) assert called(Pleroma.Web.Push.send(:_)) + + # Ensure we scheduled a final refresh of the poll + assert_enqueued( + worker: PollWorker, + args: %{"op" => "refresh", "activity_id" => activity.id} + ) end end end From a2e7db43aa3636569f4d770df980347a03c957fe Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:23:04 -0400 Subject: [PATCH 11/69] Rename assignment for consistency --- lib/pleroma/workers/poll_worker.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 574daa9ba..f70ab48a4 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -36,8 +36,8 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do with {_, %Activity{object: object}} <- {:activity, Activity.get_by_id_with_object(activity_id)}, - {:ok, naive_closed} <- NaiveDateTime.from_iso8601(object.data["closed"]), - {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, naive_closed)}, + {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]), + {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)}, {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do stream_update(activity_id) From 766edfe5b2b19f4819704540341b8fcc92f133bd Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:32:28 -0400 Subject: [PATCH 12/69] Test Poll refresh jobs stream out updates after refetching the object --- test/pleroma/workers/poll_worker_test.exs | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index e1c67f057..56a338bac 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -52,4 +52,33 @@ defmodule Pleroma.Workers.PollWorkerTest do ) end end + + test "poll refresh job" do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question) + + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert() + + expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} + + assert_enqueued(args: expected_job_args) + + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + } + ]) do + [job] = all_enqueued(worker: PollWorker) + PollWorker.perform(job) + + # Ensure updates are streamed out + assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_)) + end + end end From b2340b5b776d243f6cf12971393783cc3b7c2dc2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:45:13 -0400 Subject: [PATCH 13/69] Permit backdating the poll closed timestamp --- test/support/factory.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/support/factory.ex b/test/support/factory.ex index 8f1c6faf9..732ea3143 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -241,6 +241,7 @@ defmodule Pleroma.Factory do def question_factory(attrs \\ %{}) do user = attrs[:user] || insert(:user) + closed = attrs[:closed] || DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601() data = %{ "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), @@ -251,7 +252,7 @@ defmodule Pleroma.Factory do "to" => ["https://www.w3.org/ns/activitystreams#Public"], "cc" => [user.follower_address], "context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(), - "closed" => DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601(), + "closed" => closed, "content" => "Which flavor of ice cream do you prefer?", "oneOf" => [ %{ From a1b384f63c3587d0463109b74b0bbcc5c5ae82ee Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:45:41 -0400 Subject: [PATCH 14/69] Test that a poll refresh is cancelled if updated_at on the object is newer than the poll closing time --- test/pleroma/workers/poll_worker_test.exs | 61 +++++++++++++++-------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index 56a338bac..0fafcae11 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -53,32 +53,51 @@ defmodule Pleroma.Workers.PollWorkerTest do end end - test "poll refresh job" do - user = insert(:user, local: false) - question = insert(:question, user: user) - activity = insert(:question_activity, question: question) + describe "poll refresh" do + test "normal job" do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question) - PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert() + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert() - expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} + expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} - assert_enqueued(args: expected_job_args) + assert_enqueued(args: expected_job_args) + + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + } + ]) do + [job] = all_enqueued(worker: PollWorker) + PollWorker.perform(job) + + # Ensure updates are streamed out + assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_)) + end + end + + test "when updated_at is after poll closing" do + poll_closed = DateTime.utc_now() |> DateTime.add(-86_400) |> DateTime.to_iso8601() + user = insert(:user, local: false) + question = insert(:question, user: user, closed: poll_closed) + activity = insert(:question_activity, question: question) + + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert() + + expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} + + assert_enqueued(args: expected_job_args) - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - } - ]) do [job] = all_enqueued(worker: PollWorker) - PollWorker.perform(job) - - # Ensure updates are streamed out - assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_)) + assert {:cancel, :poll_finalized} = PollWorker.perform(job) end end end From 2ab4049508148756076853bae26279b698740597 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:47:30 -0400 Subject: [PATCH 15/69] Poll refreshing changelog --- changelog.d/poll-refresh.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/poll-refresh.change diff --git a/changelog.d/poll-refresh.change b/changelog.d/poll-refresh.change new file mode 100644 index 000000000..b755128a1 --- /dev/null +++ b/changelog.d/poll-refresh.change @@ -0,0 +1 @@ +Poll results refreshing is handled asynchronously and will not attempt to keep fetching updates to a closed poll. From b735d9e6e19a1c64f43428e6342e3d172728c736 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 14:55:38 -0400 Subject: [PATCH 16/69] Improve assertion --- test/pleroma/workers/poll_worker_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index 0fafcae11..c34647f1b 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -97,7 +97,7 @@ defmodule Pleroma.Workers.PollWorkerTest do assert_enqueued(args: expected_job_args) [job] = all_enqueued(worker: PollWorker) - assert {:cancel, :poll_finalized} = PollWorker.perform(job) + assert {:cancel, :poll_finalized} == PollWorker.perform(job) end end end From 9ff57946e7d6fa7dabaf90457e11041ce46991c4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 30 Sep 2024 15:25:13 -0400 Subject: [PATCH 17/69] Credo --- lib/pleroma/web/mastodon_api/controllers/poll_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 0d5a57518..f89bfa7f2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -9,10 +9,10 @@ defmodule Pleroma.Web.MastodonAPI.PollController do alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Workers.PollWorker alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Workers.PollWorker action_fallback(Pleroma.Web.MastodonAPI.FallbackController) From 0a42a3f2eaf53fa87d934226874de5919320de26 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 2 Oct 2024 11:05:17 -0400 Subject: [PATCH 18/69] Do not attempt to schedule poll refresh jobs for local activities --- .../controllers/poll_controller.ex | 6 ++++-- .../controllers/poll_controller_test.exs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index f89bfa7f2..495f89278 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -34,8 +34,10 @@ defmodule Pleroma.Web.MastodonAPI.PollController do with %Object{} = object <- Object.get_by_id(id), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), true <- Visibility.visible_for_user?(activity, user) do - PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert(unique: [period: 60]) + unless activity.local do + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert(unique: [period: 60]) + end try_render(conn, "show.json", %{object: object, for: user}) else diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs index b2cceec51..4b236678c 100644 --- a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs @@ -29,6 +29,25 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do id = to_string(object.id) assert %{"id" => ^id, "expired" => false, "multiple" => false} = response + refute_enqueued( + worker: Pleroma.Workers.PollWorker, + args: %{"op" => "refresh", "activity_id" => activity.id} + ) + end + + test "does not create oban job to refresh poll if activity is local", %{conn: conn} do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question, local: false) + + # Ensure this is not represented as a local activity + refute activity.local + + object = Object.normalize(activity, fetch: false) + + get(conn, "/api/v1/polls/#{object.id}") + |> json_response_and_validate_schema(200) + assert_enqueued( worker: Pleroma.Workers.PollWorker, args: %{"op" => "refresh", "activity_id" => activity.id} From 35bd1977335a2bf73207f22aecbaead6e3112a1c Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 2 Oct 2024 18:39:14 -0400 Subject: [PATCH 19/69] Fix nonexisting user will not generate metadata for search engine opt-out --- changelog.d/se-opt-out.change | 1 + lib/pleroma/web/fallback/redirect_controller.ex | 2 +- lib/pleroma/web/feed/user_controller.ex | 4 ++-- lib/pleroma/web/metadata/providers/feed.ex | 3 +++ lib/pleroma/web/metadata/providers/open_graph.ex | 3 +++ lib/pleroma/web/metadata/providers/rel_me.ex | 3 +++ lib/pleroma/web/metadata/providers/twitter_card.ex | 3 +++ test/pleroma/web/fallback_test.exs | 2 +- test/pleroma/web/feed/user_controller_test.exs | 9 +++++++++ 9 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 changelog.d/se-opt-out.change diff --git a/changelog.d/se-opt-out.change b/changelog.d/se-opt-out.change new file mode 100644 index 000000000..dd694033f --- /dev/null +++ b/changelog.d/se-opt-out.change @@ -0,0 +1 @@ +Fix nonexisting user will not generate metadata for search engine opt-out diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 4a0885fab..6637848a9 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -46,7 +46,7 @@ defmodule Pleroma.Web.Fallback.RedirectController do redirector_with_meta(conn, %{user: user}) else nil -> - redirector(conn, params) + redirector_with_meta(conn, Map.delete(params, "maybe_nickname_or_id")) end end diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index 6657c2b3e..304313068 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -15,11 +15,11 @@ defmodule Pleroma.Web.Feed.UserController do action_fallback(:errors) - def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do + def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname} = params) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, %{user: user}) else - _ -> Pleroma.Web.Fallback.RedirectController.redirector(conn, nil) + _ -> Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, params) end end diff --git a/lib/pleroma/web/metadata/providers/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex index e97d6a54f..eb84b267f 100644 --- a/lib/pleroma/web/metadata/providers/feed.ex +++ b/lib/pleroma/web/metadata/providers/feed.ex @@ -20,4 +20,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do ], []} ] end + + @impl Provider + def build_tags(_), do: [] end diff --git a/lib/pleroma/web/metadata/providers/open_graph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex index 97d3865ed..fa5fbe553 100644 --- a/lib/pleroma/web/metadata/providers/open_graph.ex +++ b/lib/pleroma/web/metadata/providers/open_graph.ex @@ -67,6 +67,9 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do end end + @impl Provider + def build_tags(_), do: [] + defp build_attachments(%{data: %{"attachment" => attachments}}) do Enum.reduce(attachments, [], fn attachment, acc -> rendered_tags = diff --git a/lib/pleroma/web/metadata/providers/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex index eabd8cb00..39aa71f06 100644 --- a/lib/pleroma/web/metadata/providers/rel_me.ex +++ b/lib/pleroma/web/metadata/providers/rel_me.ex @@ -20,6 +20,9 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do end) end + @impl Provider + def build_tags(_), do: [] + defp append_fields_tag(bio, fields) do fields |> Enum.reduce(bio, fn %{"value" => v}, res -> res <> v end) diff --git a/lib/pleroma/web/metadata/providers/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex index 426022c65..7f50877c3 100644 --- a/lib/pleroma/web/metadata/providers/twitter_card.ex +++ b/lib/pleroma/web/metadata/providers/twitter_card.ex @@ -44,6 +44,9 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do end end + @impl Provider + def build_tags(_), do: [] + defp title_tag(user) do {:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []} end diff --git a/test/pleroma/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs index ed34d6490..9184cf8f1 100644 --- a/test/pleroma/web/fallback_test.exs +++ b/test/pleroma/web/fallback_test.exs @@ -32,7 +32,7 @@ defmodule Pleroma.Web.FallbackTest do resp = get(conn, "/foo") assert html_response(resp, 200) =~ "a cool title" - refute html_response(resp, 200) =~ "initial-results" + assert html_response(resp, 200) =~ "" end test "GET /*path", %{conn: conn} do diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index 1c17d47b4..0a3aaff5c 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -147,6 +147,15 @@ defmodule Pleroma.Web.Feed.UserControllerTest do assert response(conn, 404) end + test "returns noindex meta for missing user", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/nonexisting") + + assert html_response(conn, 200) =~ "" + end + test "returns feed with public and unlisted activities", %{conn: conn} do user = insert(:user) From ba2ae5e40bbe98d20be083d331222a9aea8b61de Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 3 Oct 2024 10:14:02 -0400 Subject: [PATCH 20/69] Check if a refresh is permitted by comparing timestamps before attempting to insert an Oban job It's better to avoid inserting an Oban job that will just be rejected if it's not expensive to check. --- .../mastodon_api/controllers/poll_controller.ex | 17 ++++++++++++----- lib/pleroma/workers/poll_worker.ex | 3 --- .../controllers/poll_controller_test.exs | 5 ++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 495f89278..4b347a6a7 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -32,12 +32,10 @@ defmodule Pleroma.Web.MastodonAPI.PollController do @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with %Object{} = object <- Object.get_by_id(id), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), + %Activity{} = activity <- + Activity.get_create_by_object_ap_id_with_object(object.data["id"]), true <- Visibility.visible_for_user?(activity, user) do - unless activity.local do - PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert(unique: [period: 60]) - end + maybe_refresh_poll(activity) try_render(conn, "show.json", %{object: object, for: user}) else @@ -76,4 +74,13 @@ defmodule Pleroma.Web.MastodonAPI.PollController do end end) end + + defp maybe_refresh_poll(%Activity{object: %Object{} = object} = activity) do + with false <- activity.local, + {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]), + {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert(unique: [period: 60]) + end + end end diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index f70ab48a4..bb92634c9 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -36,8 +36,6 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do with {_, %Activity{object: object}} <- {:activity, Activity.get_by_id_with_object(activity_id)}, - {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]), - {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)}, {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do stream_update(activity_id) @@ -45,7 +43,6 @@ defmodule Pleroma.Workers.PollWorker do else {:activity, nil} -> {:cancel, :poll_activity_not_found} {:refetch, _} = e -> {:cancel, e} - {:closed_compare, _} -> {:cancel, :poll_finalized} e -> {:error, e} end end diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs index 4b236678c..51af87742 100644 --- a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs @@ -29,13 +29,16 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do id = to_string(object.id) assert %{"id" => ^id, "expired" => false, "multiple" => false} = response + # Local activities should not generate an Oban job to refresh + assert activity.local + refute_enqueued( worker: Pleroma.Workers.PollWorker, args: %{"op" => "refresh", "activity_id" => activity.id} ) end - test "does not create oban job to refresh poll if activity is local", %{conn: conn} do + test "creates an oban job to refresh poll if activity is remote", %{conn: conn} do user = insert(:user, local: false) question = insert(:question, user: user) activity = insert(:question_activity, question: question, local: false) From fa8de790dfbdb2cc7de212be4ecdd2823048ba8f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 3 Oct 2024 10:19:10 -0400 Subject: [PATCH 21/69] Remove test superceded by logic change We will not be inserting jobs that should be skipped due to updated_at --- test/pleroma/workers/poll_worker_test.exs | 61 ++++++++--------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index c34647f1b..70eb7c422 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -53,51 +53,32 @@ defmodule Pleroma.Workers.PollWorkerTest do end end - describe "poll refresh" do - test "normal job" do - user = insert(:user, local: false) - question = insert(:question, user: user) - activity = insert(:question_activity, question: question) + test "poll refresh" do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question) - PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert() + PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) + |> Oban.insert() - expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} + expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} - assert_enqueued(args: expected_job_args) - - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - } - ]) do - [job] = all_enqueued(worker: PollWorker) - PollWorker.perform(job) - - # Ensure updates are streamed out - assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_)) - end - end - - test "when updated_at is after poll closing" do - poll_closed = DateTime.utc_now() |> DateTime.add(-86_400) |> DateTime.to_iso8601() - user = insert(:user, local: false) - question = insert(:question, user: user, closed: poll_closed) - activity = insert(:question_activity, question: question) - - PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert() - - expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"} - - assert_enqueued(args: expected_job_args) + assert_enqueued(args: expected_job_args) + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + } + ]) do [job] = all_enqueued(worker: PollWorker) - assert {:cancel, :poll_finalized} == PollWorker.perform(job) + PollWorker.perform(job) + + # Ensure updates are streamed out + assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_)) end end end From b854e3836fd22a2589a6a6b97478998675d72048 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 3 Oct 2024 10:30:32 -0400 Subject: [PATCH 22/69] Remove pattern that can never match --- lib/pleroma/workers/poll_worker.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index bb92634c9..7d69bea54 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -43,7 +43,6 @@ defmodule Pleroma.Workers.PollWorker do else {:activity, nil} -> {:cancel, :poll_activity_not_found} {:refetch, _} = e -> {:cancel, e} - e -> {:error, e} end end From a3038aa6a2189ced1e5c394a4e6e8be76f2644d0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 3 Oct 2024 11:01:33 -0400 Subject: [PATCH 23/69] Increase poll refresh interval to 120 seconds --- lib/pleroma/web/mastodon_api/controllers/poll_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 4b347a6a7..6526457df 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -28,6 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @poll_refresh_interval 120 @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do @@ -80,7 +81,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]), {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id}) - |> Oban.insert(unique: [period: 60]) + |> Oban.insert(unique: [period: @poll_refresh_interval]) end end end From 4533f171ab5b73e5fc332c8f65fcf1e39e4d6003 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 5 Nov 2022 13:56:56 -0500 Subject: [PATCH 24/69] Add RemoteReportPolicy to reject reports without enough information --- config/config.exs | 4 + .../activity_pub/mrf/remote_report_policy.ex | 80 +++++++++++++++++ .../mrf/remote_report_policy_test.exs | 85 +++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex create mode 100644 test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs diff --git a/config/config.exs b/config/config.exs index 47ddfac5a..203a61c75 100644 --- a/config/config.exs +++ b/config/config.exs @@ -434,6 +434,10 @@ config :pleroma, :mrf_follow_bot, follower_nickname: nil config :pleroma, :mrf_inline_quote, template: "RT: {url}" +config :pleroma, :mrf_remote_report, + reject_anonymous: true, + reject_empty_message: true + config :pleroma, :mrf_force_mention, mention_parent: true, mention_quoted: true diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex new file mode 100644 index 000000000..3cf47e3ed --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -0,0 +1,80 @@ +defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do + @moduledoc "Drop remote reports if they don't contain enough information." + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + alias Pleroma.Config + + @impl true + def filter(%{"type" => "Flag"} = object) do + with {_, false} <- {:local, local?(object)}, + {:ok, _} <- maybe_reject_anonymous(object), + {:ok, _} <- maybe_reject_empty_message(object) do + {:ok, object} + else + {:local, true} -> {:ok, object} + {:reject, message} -> {:reject, message} + error -> {:reject, error} + end + end + + def filter(object), do: {:ok, object} + + defp maybe_reject_anonymous(%{"actor" => actor} = object) do + with true <- Config.get([:mrf_remote_report, :reject_anonymous]), + %URI{path: "/actor"} <- URI.parse(actor) do + {:reject, "[RemoteReportPolicy] Anonymous: #{actor}"} + else + _ -> {:ok, object} + end + end + + defp maybe_reject_empty_message(%{"content" => content} = object) + when is_binary(content) and content != "" do + {:ok, object} + end + + defp maybe_reject_empty_message(object) do + if Config.get([:mrf_remote_report, :reject_empty_message]) do + {:reject, ["RemoteReportPolicy] No content"]} + else + {:ok, object} + end + end + + defp local?(%{"actor" => actor}) do + String.starts_with?(actor, Pleroma.Web.Endpoint.url()) + end + + @impl true + def describe do + mrf_remote_report = + Config.get(:mrf_remote_report) + |> Enum.into(%{}) + + {:ok, %{mrf_remote_report: mrf_remote_report}} + end + + @impl true + def config_description do + %{ + key: :mrf_remote_report, + related_policy: "Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy", + label: "MRF Remote Report", + description: "Drop remote reports if they don't contain enough information.", + children: [ + %{ + key: :reject_anonymous, + type: :boolean, + description: "Reject anonymous remote reports?", + suggestions: [true] + }, + %{ + key: :reject_empty_message, + type: :boolean, + description: "Reject remote reports with no message?", + suggestions: [true] + } + ] + } + end +end diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs new file mode 100644 index 000000000..55fa0f2f2 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs @@ -0,0 +1,85 @@ +defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy + + test "doesn't impact local report" do + clear_config([:mrf_remote_report, :reject_anonymous], true) + clear_config([:mrf_remote_report, :reject_empty_message], true) + + activity = %{ + "type" => "Flag", + "actor" => "http://localhost:4001/actor" + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "rejects anonymous report if `reject_anonymous: true`" do + clear_config([:mrf_remote_report, :reject_anonymous], true) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/actor" + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves anonymous report if `reject_anonymous: false`" do + clear_config([:mrf_remote_report, :reject_anonymous], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/actor" + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "rejects empty message report if `reject_empty_message: true`" do + clear_config([:mrf_remote_report, :reject_empty_message], true) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron" + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end + + test "rejects empty message report (\"\") if `reject_empty_message: true`" do + clear_config([:mrf_remote_report, :reject_empty_message], true) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "content" => "" + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves empty message report if `reject_empty_message: false`" do + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron" + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves anonymous, empty message report with all settings disabled" do + clear_config([:mrf_remote_report, :reject_empty_message], false) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/actor" + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end +end From b7c91876d2cc027a5a7f8a79ba256f13af623997 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 5 Nov 2022 14:07:37 -0500 Subject: [PATCH 25/69] RemoteReportPolicy: add `:reject_all` option, fix tests --- config/config.exs | 1 + .../activity_pub/mrf/remote_report_policy.ex | 15 +++++++++++ .../mrf/remote_report_policy_test.exs | 25 ++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index 203a61c75..07e98011d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -435,6 +435,7 @@ config :pleroma, :mrf_follow_bot, follower_nickname: nil config :pleroma, :mrf_inline_quote, template: "RT: {url}" config :pleroma, :mrf_remote_report, + reject_all: false, reject_anonymous: true, reject_empty_message: true diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index 3cf47e3ed..0bd83d8f0 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do @impl true def filter(%{"type" => "Flag"} = object) do with {_, false} <- {:local, local?(object)}, + {:ok, _} <- maybe_reject_all(object), {:ok, _} <- maybe_reject_anonymous(object), {:ok, _} <- maybe_reject_empty_message(object) do {:ok, object} @@ -19,6 +20,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do def filter(object), do: {:ok, object} + defp maybe_reject_all(object) do + if Config.get([:mrf_remote_report, :reject_all]) do + {:reject, "[RemoteReportPolicy] Remote report"} + else + {:ok, object} + end + end + defp maybe_reject_anonymous(%{"actor" => actor} = object) do with true <- Config.get([:mrf_remote_report, :reject_anonymous]), %URI{path: "/actor"} <- URI.parse(actor) do @@ -62,6 +71,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do label: "MRF Remote Report", description: "Drop remote reports if they don't contain enough information.", children: [ + %{ + key: :reject_all, + type: :boolean, + description: "Reject all remote reports? (this option takes precedence)", + suggestions: [false] + }, %{ key: :reject_anonymous, type: :boolean, diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs index 55fa0f2f2..43258a7f6 100644 --- a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs @@ -3,6 +3,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy + setup do + clear_config([:mrf_remote_report, :reject_all], false) + end + test "doesn't impact local report" do clear_config([:mrf_remote_report, :reject_anonymous], true) clear_config([:mrf_remote_report, :reject_empty_message], true) @@ -17,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do test "rejects anonymous report if `reject_anonymous: true`" do clear_config([:mrf_remote_report, :reject_anonymous], true) + clear_config([:mrf_remote_report, :reject_empty_message], true) activity = %{ "type" => "Flag", @@ -28,6 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do test "preserves anonymous report if `reject_anonymous: false`" do clear_config([:mrf_remote_report, :reject_anonymous], false) + clear_config([:mrf_remote_report, :reject_empty_message], false) activity = %{ "type" => "Flag", @@ -38,6 +44,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do end test "rejects empty message report if `reject_empty_message: true`" do + clear_config([:mrf_remote_report, :reject_anonymous], false) clear_config([:mrf_remote_report, :reject_empty_message], true) activity = %{ @@ -49,6 +56,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do end test "rejects empty message report (\"\") if `reject_empty_message: true`" do + clear_config([:mrf_remote_report, :reject_anonymous], false) clear_config([:mrf_remote_report, :reject_empty_message], true) activity = %{ @@ -61,6 +69,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do end test "preserves empty message report if `reject_empty_message: false`" do + clear_config([:mrf_remote_report, :reject_anonymous], false) clear_config([:mrf_remote_report, :reject_empty_message], false) activity = %{ @@ -72,7 +81,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do end test "preserves anonymous, empty message report with all settings disabled" do - clear_config([:mrf_remote_report, :reject_empty_message], false) + clear_config([:mrf_remote_report, :reject_anonymous], false) clear_config([:mrf_remote_report, :reject_empty_message], false) activity = %{ @@ -82,4 +91,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do assert {:ok, _} = RemoteReportPolicy.filter(activity) end + + test "reject remote report if `reject_all: true`" do + clear_config([:mrf_remote_report, :reject_all], true) + clear_config([:mrf_remote_report, :reject_anonymous], false) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "content" => "Transphobia" + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end end From fd83b86b99ee6642fa0a765a55c0f0e35f272151 Mon Sep 17 00:00:00 2001 From: Mint Date: Tue, 12 Mar 2024 22:45:15 +0300 Subject: [PATCH 26/69] RemoteReportPolicy: add `reject_third_party` option --- .../activity_pub/mrf/remote_report_policy.ex | 22 +++++++++ .../mrf/remote_report_policy_test.exs | 48 ++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index 0bd83d8f0..964c59cbf 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do with {_, false} <- {:local, local?(object)}, {:ok, _} <- maybe_reject_all(object), {:ok, _} <- maybe_reject_anonymous(object), + {:ok, _} <- maybe_reject_third_party(object), {:ok, _} <- maybe_reject_empty_message(object) do {:ok, object} else @@ -37,6 +38,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do end end + defp maybe_reject_third_party(%{"object" => objects} = object) do + {_, to} = case objects do + [head | tail] when is_binary(head) -> {tail, head} + s when is_binary(s) -> {[], s} + _ -> {[], ""} + end + + with true <- Config.get([:mrf_remote_report, :reject_third_party]), + String.starts_with?(to, Pleroma.Web.Endpoint.url()) do + {:reject, "[RemoteReportPolicy] Third-party: #{to}"} + else + _ -> {:ok, object} + end + end + defp maybe_reject_empty_message(%{"content" => content} = object) when is_binary(content) and content != "" do {:ok, object} @@ -83,6 +99,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do description: "Reject anonymous remote reports?", suggestions: [true] }, + %{ + key: :reject_third_party, + type: :boolean, + description: "Reject reports on users from third-party instances?", + suggestions: [true] + }, %{ key: :reject_empty_message, type: :boolean, diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs index 43258a7f6..dd56a1e9b 100644 --- a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs @@ -13,7 +13,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "http://localhost:4001/actor" + "actor" => "http://localhost:4001/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -25,7 +26,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) @@ -37,7 +39,34 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "rejects report on third-party if `reject_third_party: true`" do + clear_config([:mrf_remote_report, :reject_third_party], true) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves report on third party if `reject_third_party: false`" do + clear_config([:mrf_remote_report, :reject_third_party], false) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -49,7 +78,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/users/Gargron" + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) @@ -62,6 +92,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"], "content" => "" } @@ -74,7 +105,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/users/Gargron" + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -86,7 +118,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -100,7 +133,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do activity = %{ "type" => "Flag", "actor" => "https://mastodon.social/users/Gargron", - "content" => "Transphobia" + "content" => "Transphobia", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) From 55612cb8ee4908a2fbb200ff581bb07c7e43410a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 12 Mar 2024 15:52:33 -0500 Subject: [PATCH 27/69] mix format --- .../web/activity_pub/mrf/remote_report_policy.ex | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index 964c59cbf..d33028931 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -39,11 +39,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do end defp maybe_reject_third_party(%{"object" => objects} = object) do - {_, to} = case objects do - [head | tail] when is_binary(head) -> {tail, head} - s when is_binary(s) -> {[], s} - _ -> {[], ""} - end + {_, to} = + case objects do + [head | tail] when is_binary(head) -> {tail, head} + s when is_binary(s) -> {[], s} + _ -> {[], ""} + end with true <- Config.get([:mrf_remote_report, :reject_third_party]), String.starts_with?(to, Pleroma.Web.Endpoint.url()) do From 48af6850fc2903d6f8c7cbf43b7db6b769c37a2a Mon Sep 17 00:00:00 2001 From: Mint Date: Fri, 12 Apr 2024 23:04:37 +0300 Subject: [PATCH 28/69] RemoteReportPolicy: Fix third-party report detection --- .../web/activity_pub/mrf/remote_report_policy.ex | 2 +- .../mrf/remote_report_policy_test.exs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index d33028931..fa0610bf1 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do end with true <- Config.get([:mrf_remote_report, :reject_third_party]), - String.starts_with?(to, Pleroma.Web.Endpoint.url()) do + false <- String.starts_with?(to, Pleroma.Web.Endpoint.url()) do {:reject, "[RemoteReportPolicy] Third-party: #{to}"} else _ -> {:ok, object} diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs index dd56a1e9b..8d2a6b4fa 100644 --- a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do assert {:ok, _} = RemoteReportPolicy.filter(activity) end - test "rejects report on third-party if `reject_third_party: true`" do + test "rejects report on third party if `reject_third_party: true`" do clear_config([:mrf_remote_report, :reject_third_party], true) clear_config([:mrf_remote_report, :reject_empty_message], false) @@ -59,6 +59,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do assert {:reject, _} = RemoteReportPolicy.filter(activity) end + test "preserves report on first party if `reject_third_party: true`" do + clear_config([:mrf_remote_report, :reject_third_party], true) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["http://localhost:4001/actor"] + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + test "preserves report on third party if `reject_third_party: false`" do clear_config([:mrf_remote_report, :reject_third_party], false) clear_config([:mrf_remote_report, :reject_empty_message], false) From eb971aa022f524f364daf24d6e6d617bfc5ca036 Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 3 Oct 2024 20:02:58 +0300 Subject: [PATCH 29/69] Changelog --- changelog.d/remote-report-policy.add | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/remote-report-policy.add diff --git a/changelog.d/remote-report-policy.add b/changelog.d/remote-report-policy.add new file mode 100644 index 000000000..1cf25b1a8 --- /dev/null +++ b/changelog.d/remote-report-policy.add @@ -0,0 +1 @@ +Added RemoteReportPolicy from Rebased for handling bogus federated reports From 0c41d986de973bfae82794b6fe499f8261a2f6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 6 Oct 2024 17:00:39 +0200 Subject: [PATCH 30/69] Metadata: Do not include .atom feed links for remote accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/atom-tag.change | 1 + lib/pleroma/web/metadata/providers/feed.ex | 4 +++- test/pleroma/web/metadata/providers/feed_test.exs | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelog.d/atom-tag.change diff --git a/changelog.d/atom-tag.change b/changelog.d/atom-tag.change new file mode 100644 index 000000000..1b3590dea --- /dev/null +++ b/changelog.d/atom-tag.change @@ -0,0 +1 @@ +Metadata: Do not include .atom feed links for remote accounts diff --git a/lib/pleroma/web/metadata/providers/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex index e97d6a54f..3811f96f6 100644 --- a/lib/pleroma/web/metadata/providers/feed.ex +++ b/lib/pleroma/web/metadata/providers/feed.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do @behaviour Provider @impl Provider - def build_tags(%{user: user}) do + def build_tags(%{user: %{local: true} = user}) do [ {:link, [ @@ -20,4 +20,6 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do ], []} ] end + + def build_tags(_), do: [] end diff --git a/test/pleroma/web/metadata/providers/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs index e593453da..40d9d0909 100644 --- a/test/pleroma/web/metadata/providers/feed_test.exs +++ b/test/pleroma/web/metadata/providers/feed_test.exs @@ -15,4 +15,10 @@ defmodule Pleroma.Web.Metadata.Providers.FeedTest do [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []} ] end + + test "it doesn't render a link to remote user's feed" do + user = insert(:user, nickname: "lain@lain.com", local: false) + + assert Feed.build_tags(%{user: user}) == [] + end end From f758b6e37c80f5adeba74009e1cc72a420937a30 Mon Sep 17 00:00:00 2001 From: tusooa Date: Tue, 8 Oct 2024 23:09:59 -0400 Subject: [PATCH 31/69] Fix incoming Blocks being rejected --- changelog.d/incoming-blocks.fix | 1 + lib/pleroma/constants.ex | 5 +++++ .../web/activity_pub/object_validator.ex | 12 +++++++++++ .../activity_pub_controller_test.exs | 21 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 changelog.d/incoming-blocks.fix diff --git a/changelog.d/incoming-blocks.fix b/changelog.d/incoming-blocks.fix new file mode 100644 index 000000000..3228d7318 --- /dev/null +++ b/changelog.d/incoming-blocks.fix @@ -0,0 +1 @@ +Fix incoming Block activities being rejected diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index 5268ebe7a..2828c79a9 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -87,6 +87,7 @@ defmodule Pleroma.Constants do const(activity_types, do: [ + "Block", "Create", "Update", "Delete", @@ -115,6 +116,10 @@ defmodule Pleroma.Constants do ] ) + const(object_types, + do: ~w[Event Question Answer Audio Video Image Article Note Page ChatMessage] + ) + # basic regex, just there to weed out potential mistakes # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 const(mime_regex, diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index b3043b93a..35774d410 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating + import Pleroma.Constants, only: [activity_types: 0, object_types: 0] + alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object @@ -38,6 +40,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @impl true def validate(object, meta) + # This overload works together with the InboxGuardPlug + # and ensures that we are not accepting any activity type + # that cannot pass InboxGuardPlug. + # If we want to support any more activity types, make sure to + # add it in Pleroma.Constants's activity_types or object_types, + # and, if applicable, allowed_activity_types_from_strangers. + def validate(%{"type" => type}, _meta) + when type not in activity_types() and type not in object_types(), + do: {:error, :not_allowed_object_type} + def validate(%{"type" => "Block"} = block_activity, meta) do with {:ok, block_activity} <- block_activity diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 3bd589f49..d4175b56f 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -1320,6 +1320,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do html_body: ~r/#{note.data["object"]}/i ) end + + test "it accepts an incoming Block", %{conn: conn, data: data} do + user = insert(:user) + + data = + data + |> Map.put("type", "Block") + |> Map.put("to", [user.ap_id]) + |> Map.put("cc", []) + |> Map.put("object", user.ap_id) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end end describe "GET /users/:nickname/outbox" do From 37b1192b7bf7aa98fef0ca6c06d5b53719cb2e7b Mon Sep 17 00:00:00 2001 From: fzorb fzorbius Date: Wed, 9 Oct 2024 18:33:22 +0000 Subject: [PATCH 32/69] Should probably also include vips in the media/graphics packages section, as you need it to compile some library --- docs/installation/freebsd_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md index 02513daf2..920bc7d35 100644 --- a/docs/installation/freebsd_en.md +++ b/docs/installation/freebsd_en.md @@ -31,7 +31,7 @@ Setup the required services to automatically start at boot, using `sysrc(8)`. ### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)) ```shell -# pkg install imagemagick ffmpeg p5-Image-ExifTool +# pkg install imagemagick ffmpeg p5-Image-ExifTool vips ``` ## Configuring Pleroma From 03a6e33b81281256f2e9b6ffb75910fdd1a7894f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 9 Oct 2024 16:25:58 -0400 Subject: [PATCH 33/69] Skip the final refresh job if the activity is local --- lib/pleroma/workers/poll_worker.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 7d69bea54..a9afe9d63 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -22,9 +22,11 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)}, {:ok, notifications} <- Notification.create_poll_notifications(activity) do - # Schedule a final refresh - __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id}) - |> Oban.insert() + unless activity.local do + # Schedule a final refresh + __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id}) + |> Oban.insert() + end Notification.stream(notifications) else From 5b04c2bf131f70120c407f5b4c242e3d245151f8 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 9 Oct 2024 20:15:00 -0400 Subject: [PATCH 34/69] Test the final refresh behavior of a PollWorker poll_end job --- test/pleroma/workers/poll_worker_test.exs | 32 ++++++++++++++++++++--- test/support/factory.ex | 3 ++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index 70eb7c422..a7cbbdb83 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -11,10 +11,10 @@ defmodule Pleroma.Workers.PollWorkerTest do alias Pleroma.Workers.PollWorker - test "poll notification job" do + test "local poll ending notification job" do user = insert(:user) question = insert(:question, user: user) - activity = insert(:question_activity, question: question) + activity = insert(:question_activity, question: question, user: user) PollWorker.schedule_poll_end(activity) @@ -45,14 +45,38 @@ defmodule Pleroma.Workers.PollWorkerTest do assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_)) assert called(Pleroma.Web.Push.send(:_)) - # Ensure we scheduled a final refresh of the poll - assert_enqueued( + # Skip refreshing polls for local activities + assert activity.local + + refute_enqueued( worker: PollWorker, args: %{"op" => "refresh", "activity_id" => activity.id} ) end end + test "remote poll ending notification job schedules refresh" do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question, user: user) + + PollWorker.schedule_poll_end(activity) + + expected_job_args = %{"activity_id" => activity.id, "op" => "poll_end"} + + assert_enqueued(args: expected_job_args) + + [job] = all_enqueued(worker: PollWorker) + PollWorker.perform(job) + + refute activity.local + + assert_enqueued( + worker: PollWorker, + args: %{"op" => "refresh", "activity_id" => activity.id} + ) + end + test "poll refresh" do user = insert(:user, local: false) question = insert(:question, user: user) diff --git a/test/support/factory.ex b/test/support/factory.ex index 732ea3143..91e5805c8 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -510,7 +510,8 @@ defmodule Pleroma.Factory do %Pleroma.Activity{ data: data, actor: data["actor"], - recipients: data["to"] + recipients: data["to"], + local: user.local } |> Map.merge(attrs) end From 23f78c75738aac49a79de2834490048cde817669 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 14:27:11 -0400 Subject: [PATCH 35/69] Refactor password changes to go through Pleroma.Web.Auth so they can be supported by the different auth backends --- lib/pleroma/web/auth/authenticator.ex | 5 ++++ lib/pleroma/web/auth/pleroma_authenticator.ex | 20 +++++++++++++ lib/pleroma/web/auth/wrapper_authenticator.ex | 4 +++ .../controllers/util_controller.ex | 29 ++++++++++--------- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index 01bf1575c..95be892cd 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do @callback handle_error(Plug.Conn.t(), any()) :: any() @callback auth_template() :: String.t() | nil @callback oauth_consumer_template() :: String.t() | nil + + @callback change_password(Pleroma.User.t(), String.t(), String.t(), String.t()) :: + {:ok, Pleroma.User.t()} | {:error, term()} + + @optional_callbacks change_password: 4 end diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index 09a58eb66..0da3f19fc 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.AuthenticationPlug import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1] @@ -101,4 +102,23 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do def auth_template, do: nil def oauth_consumer_template, do: nil + + @doc "Changes Pleroma.User password in the database" + def change_password(user, password, new_password, new_password) do + case CommonAPI.Utils.confirm_current_password(user, password) do + {:ok, user} -> + with {:ok, _user} <- + User.reset_password(user, %{ + password: new_password, + password_confirmation: new_password + }) do + {:ok, user} + end + + error -> + error + end + end + + def change_password(_, _, _, _), do: {:error, :password_confirmation} end diff --git a/lib/pleroma/web/auth/wrapper_authenticator.ex b/lib/pleroma/web/auth/wrapper_authenticator.ex index a077cfa41..97b901036 100644 --- a/lib/pleroma/web/auth/wrapper_authenticator.ex +++ b/lib/pleroma/web/auth/wrapper_authenticator.ex @@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator do implementation().oauth_consumer_template() || Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html") end + + @impl true + def change_password(user, password, new_password, new_password_confirmation), + do: implementation().change_password(user, password, new_password, new_password_confirmation) end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 6805233df..aeafa195d 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Healthcheck alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger @@ -195,19 +196,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn, _ ) do - case CommonAPI.Utils.confirm_current_password(user, body_params.password) do - {:ok, user} -> - with {:ok, _user} <- - User.reset_password(user, %{ - password: body_params.new_password, - password_confirmation: body_params.new_password_confirmation - }) do - json(conn, %{status: "success"}) - else - {:error, changeset} -> - {_, {error, _}} = Enum.at(changeset.errors, 0) - json(conn, %{error: "New password #{error}."}) - end + with {:ok, %User{}} <- + Authenticator.change_password( + user, + body_params.password, + body_params.new_password, + body_params.new_password_confirmation + ) do + json(conn, %{status: "success"}) + else + {:error, %Ecto.Changeset{} = changeset} -> + {_, {error, _}} = Enum.at(changeset.errors, 0) + json(conn, %{error: "New password #{error}."}) + + {:error, :password_confirmation} -> + json(conn, %{error: "New password does not match confirmation."}) {:error, msg} -> json(conn, %{error: msg}) From 67cc38b5ac0cb009a38de3b182f34bbcb97467da Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 15:39:38 -0400 Subject: [PATCH 36/69] Support password changes for LDAP auth backend --- lib/pleroma/ldap.ex | 30 +++++++++++++++++++--- lib/pleroma/web/auth/ldap_authenticator.ex | 9 +++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 46a2d0c17..9c1263fcf 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -83,6 +83,12 @@ defmodule Pleroma.LDAP do end end + def handle_call({:change_password, name, password, new_password}, _from, state) do + result = change_password(state[:handle], name, password, new_password) + + {:reply, result, state, :hibernate} + end + @impl true def terminate(_, state) do handle = Keyword.get(state, :handle) @@ -162,17 +168,16 @@ defmodule Pleroma.LDAP do end defp bind_user(handle, name, password) do - uid = Config.get([:ldap, :uid], "cn") - base = Config.get([:ldap, :base]) + dn = make_dn(name) - case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do + case :eldap.simple_bind(handle, dn, password) do :ok -> case fetch_user(name) do %User{} = user -> user _ -> - register_user(handle, base, uid, name) + register_user(handle, ldap_base(), ldap_uid(), name) end # eldap does not inform us of socket closure @@ -231,6 +236,14 @@ defmodule Pleroma.LDAP do end end + defp change_password(handle, name, password, new_password) do + dn = make_dn(name) + + with :ok <- :eldap.simple_bind(handle, dn, password) do + :eldap.modify_password(handle, dn, to_charlist(new_password), to_charlist(password)) + end + end + defp decode_certfile(file) do with {:ok, data} <- File.read(file) do data @@ -242,4 +255,13 @@ defmodule Pleroma.LDAP do [] end end + + defp ldap_uid, do: to_charlist(Config.get([:ldap, :uid], "cn")) + defp ldap_base, do: to_charlist(Config.get([:ldap, :base])) + + defp make_dn(name) do + uid = ldap_uid() + base = ldap_base() + ~c"#{uid}=#{name},#{base}" + end end diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 7eb06183d..9bdf8447d 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do error end end + + def change_password(user, password, new_password, new_password) do + case GenServer.call(LDAP, {:change_password, user.nickname, password, new_password}) do + :ok -> {:ok, user} + e -> e + end + end + + def change_password(_, _, _, _), do: {:error, :password_confirmation} end From ff039f953043d2c15f1eb44f794a77865ab5a775 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 15:41:08 -0400 Subject: [PATCH 37/69] Add example OpenLDAP ldif to enable users to change their own passwords --- installation/openldap/pw_self_service.ldif | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 installation/openldap/pw_self_service.ldif diff --git a/installation/openldap/pw_self_service.ldif b/installation/openldap/pw_self_service.ldif new file mode 100644 index 000000000..463dabbfb --- /dev/null +++ b/installation/openldap/pw_self_service.ldif @@ -0,0 +1,7 @@ +dn: olcDatabase={1}mdb,cn=config +changetype: modify +add: olcAccess +olcAccess: {1}to attrs=userPassword + by self write + by anonymous auth + by * none From 6bc70b8b2a7c6942bfda01bfcc301a198cf3238b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 15:45:09 -0400 Subject: [PATCH 38/69] Add change_password/3 to LDAP module --- lib/pleroma/ldap.ex | 4 ++++ lib/pleroma/web/auth/ldap_authenticator.ex | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 9c1263fcf..2bc894bd8 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -104,6 +104,10 @@ defmodule Pleroma.LDAP do GenServer.call(__MODULE__, {:bind_user, name, password}) end + def change_password(name, password, new_password) do + GenServer.call(__MODULE__, {:change_password, name, password, new_password}) + end + defp connect do ldap = Config.get(:ldap, []) host = Keyword.get(ldap, :host, "localhost") diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 9bdf8447d..ec6601fb9 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -32,7 +32,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do end def change_password(user, password, new_password, new_password) do - case GenServer.call(LDAP, {:change_password, user.nickname, password, new_password}) do + case LDAP.change_password(user.nickname, password, new_password) do :ok -> {:ok, user} e -> e end From 1da057e6a4e4f8f7ddeb0ba286a3b996c1ba7710 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 15:51:56 -0400 Subject: [PATCH 39/69] Reorganize the LDAP module --- lib/pleroma/ldap.ex | 50 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 2bc894bd8..b591c2918 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -15,6 +15,14 @@ defmodule Pleroma.LDAP do GenServer.start_link(__MODULE__, [], name: __MODULE__) end + def bind_user(name, password) do + GenServer.call(__MODULE__, {:bind_user, name, password}) + end + + def change_password(name, password, new_password) do + GenServer.call(__MODULE__, {:change_password, name, password, new_password}) + end + @impl true def init(state) do case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do @@ -47,33 +55,16 @@ defmodule Pleroma.LDAP do def handle_info(:connect, _state), do: do_handle_connect() def handle_info({:bind_after_reconnect, name, password, from}, state) do - result = bind_user(state[:handle], name, password) + result = do_bind_user(state[:handle], name, password) GenServer.reply(from, result) {:noreply, state} end - defp do_handle_connect do - state = - case connect() do - {:ok, handle} -> - :eldap.controlling_process(handle, self()) - Process.link(handle) - [handle: handle] - - _ -> - Logger.error("Failed to connect to LDAP. Retrying in 5000ms") - Process.send_after(self(), :connect, 5_000) - [] - end - - {:noreply, state} - end - @impl true def handle_call({:bind_user, name, password}, from, state) do - case bind_user(state[:handle], name, password) do + case do_bind_user(state[:handle], name, password) do :needs_reconnect -> Process.send(self(), {:bind_after_reconnect, name, password, from}, []) {:noreply, state, {:continue, :connect}} @@ -100,12 +91,21 @@ defmodule Pleroma.LDAP do :ok end - def bind_user(name, password) do - GenServer.call(__MODULE__, {:bind_user, name, password}) - end + defp do_handle_connect do + state = + case connect() do + {:ok, handle} -> + :eldap.controlling_process(handle, self()) + Process.link(handle) + [handle: handle] - def change_password(name, password, new_password) do - GenServer.call(__MODULE__, {:change_password, name, password, new_password}) + _ -> + Logger.error("Failed to connect to LDAP. Retrying in 5000ms") + Process.send_after(self(), :connect, 5_000) + [] + end + + {:noreply, state} end defp connect do @@ -171,7 +171,7 @@ defmodule Pleroma.LDAP do end end - defp bind_user(handle, name, password) do + defp do_bind_user(handle, name, password) do dn = make_dn(name) case :eldap.simple_bind(handle, dn, password) do From b6a951cfb5e277aa265436674055a4d0b993c5b9 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Oct 2024 16:20:38 -0400 Subject: [PATCH 40/69] LDAP password changing changelog --- changelog.d/ldap-password-change.add | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/ldap-password-change.add diff --git a/changelog.d/ldap-password-change.add b/changelog.d/ldap-password-change.add new file mode 100644 index 000000000..7ca555ee4 --- /dev/null +++ b/changelog.d/ldap-password-change.add @@ -0,0 +1 @@ +LDAP now supports users changing their passwords From 60ec42cb9c5f362e01ca2fb506ac153e00d5caa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 12 Oct 2024 23:45:18 +0200 Subject: [PATCH 41/69] Add metadata provider for ActivityPub alternate links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/activity-pub-metadata.add | 1 + lib/pleroma/web/metadata.ex | 1 + .../web/metadata/providers/activity_pub.ex | 19 +++++++++++ .../metadata/providers/activity_pub_test.exs | 34 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 changelog.d/activity-pub-metadata.add create mode 100644 lib/pleroma/web/metadata/providers/activity_pub.ex create mode 100644 test/pleroma/web/metadata/providers/activity_pub_test.exs diff --git a/changelog.d/activity-pub-metadata.add b/changelog.d/activity-pub-metadata.add new file mode 100644 index 000000000..2ad3d7b2d --- /dev/null +++ b/changelog.d/activity-pub-metadata.add @@ -0,0 +1 @@ +Add metadata provider for ActivityPub alternate links diff --git a/lib/pleroma/web/metadata.ex b/lib/pleroma/web/metadata.ex index 59d018730..4ee7c41ec 100644 --- a/lib/pleroma/web/metadata.ex +++ b/lib/pleroma/web/metadata.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.Metadata do def build_tags(params) do providers = [ + Pleroma.Web.Metadata.Providers.ActivityPub, Pleroma.Web.Metadata.Providers.RelMe, Pleroma.Web.Metadata.Providers.RestrictIndexing | activated_providers() diff --git a/lib/pleroma/web/metadata/providers/activity_pub.ex b/lib/pleroma/web/metadata/providers/activity_pub.ex new file mode 100644 index 000000000..1759a5a0d --- /dev/null +++ b/lib/pleroma/web/metadata/providers/activity_pub.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.ActivityPub do + alias Pleroma.Web.Metadata.Providers.Provider + + @behaviour Provider + + @impl Provider + def build_tags(%{object: %{data: %{"id" => object_id}}}) do + [{:link, [rel: "alternate", type: "application/activity+json", href: object_id], []}] + end + + @impl Provider + def build_tags(%{user: user}) do + [{:link, [rel: "alternate", type: "application/activity+json", href: user.ap_id], []}] + end +end diff --git a/test/pleroma/web/metadata/providers/activity_pub_test.exs b/test/pleroma/web/metadata/providers/activity_pub_test.exs new file mode 100644 index 000000000..c379ec092 --- /dev/null +++ b/test/pleroma/web/metadata/providers/activity_pub_test.exs @@ -0,0 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.ActivityPubTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Metadata.Providers.ActivityPub + + setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) + + test "it renders a link for user info" do + user = insert(:user) + res = ActivityPub.build_tags(%{user: user}) + + assert res == [ + {:link, [rel: "alternate", type: "application/activity+json", href: user.ap_id], []} + ] + end + + test "it renders a link for a post" do + user = insert(:user) + {:ok, %{id: activity_id, object: object}} = CommonAPI.post(user, %{status: "hi"}) + + result = ActivityPub.build_tags(%{object: object, user: user, activity_id: activity_id}) + + assert [ + {:link, + [rel: "alternate", type: "application/activity+json", href: object.data["id"]], []} + ] == result + end +end From f048637b41a81c527b6f3c0f5e90db91971f3842 Mon Sep 17 00:00:00 2001 From: Mark Jaroski Date: Mon, 21 Oct 2024 00:10:27 +0000 Subject: [PATCH 42/69] Some tidying and grammer improvements for these installation docs, based on my experience installing Pleroma on Ubuntu 24.04 a few minutes ago. --- changelog.d/debian-install-improve.skip | 1 + docs/installation/debian_based_en.md | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 changelog.d/debian-install-improve.skip diff --git a/changelog.d/debian-install-improve.skip b/changelog.d/debian-install-improve.skip new file mode 100644 index 000000000..6068a3066 --- /dev/null +++ b/changelog.d/debian-install-improve.skip @@ -0,0 +1 @@ +Fixed a formatting issue that had a required commend embedded in a textblock, and change the language to make it a bit more idiomatic. \ No newline at end of file diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md index b61e4addd..21cfe2bff 100644 --- a/docs/installation/debian_based_en.md +++ b/docs/installation/debian_based_en.md @@ -69,12 +69,18 @@ cd /opt/pleroma sudo -Hu pleroma mix deps.get ``` -* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen` +* Generate the configuration: + +```shell +sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen` +``` + +* During this process: * Answer with `yes` if it asks you to install `rebar3`. * This may take some time, because parts of pleroma get compiled first. * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`. -* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances): +* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for production instances, `dev.secret.exs` for development instances): ```shell sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} From e1296737a69703535a3688f2dd205821f0e9d073 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 22 Sep 2024 15:19:05 -0400 Subject: [PATCH 43/69] Disable busywaits in releases --- changelog.d/release-tuning.change | 1 + rel/vm.args.eex | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 changelog.d/release-tuning.change diff --git a/changelog.d/release-tuning.change b/changelog.d/release-tuning.change new file mode 100644 index 000000000..bf9abc3ad --- /dev/null +++ b/changelog.d/release-tuning.change @@ -0,0 +1 @@ +Tuning for release builds to lower CPU usage. diff --git a/rel/vm.args.eex b/rel/vm.args.eex index 71e803264..8e38fee4b 100644 --- a/rel/vm.args.eex +++ b/rel/vm.args.eex @@ -9,3 +9,8 @@ ## Tweak GC to run more often ##-env ERL_FULLSWEEP_AFTER 10 + +# Disable wasteful busywait. ++sbwt none ++sbwtdcpu none ++sbwtdio none From dc6362f71df2edc126cbebbb7f346ff4768c8451 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 25 Oct 2024 11:38:20 -0400 Subject: [PATCH 44/69] Changelog --- changelog.d/freebsd-docs.skip | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 changelog.d/freebsd-docs.skip diff --git a/changelog.d/freebsd-docs.skip b/changelog.d/freebsd-docs.skip new file mode 100644 index 000000000..e69de29bb From 63c6dacfcecd792cacbb8f735f52aa4912f23c9c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 25 Oct 2024 11:38:20 -0400 Subject: [PATCH 45/69] Changelog --- changelog.d/docs-vips.skip | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 changelog.d/docs-vips.skip diff --git a/changelog.d/docs-vips.skip b/changelog.d/docs-vips.skip new file mode 100644 index 000000000..e69de29bb From 00b6a586acfbd60883fc1197fb5d5ed459606c9e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 25 Oct 2024 11:56:55 -0400 Subject: [PATCH 46/69] OpenBSD needs libvips Confirmed package exists by testing an OpenBSD 7.6 arm64 VM --- docs/installation/openbsd_en.md | 2 +- docs/installation/openbsd_fi.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md index e58e144d2..78bbf399f 100644 --- a/docs/installation/openbsd_en.md +++ b/docs/installation/openbsd_en.md @@ -12,7 +12,7 @@ For any additional information regarding commands and configuration files mentio To install them, run the following command (with doas or as root): ``` -pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick +pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick libvips ``` Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt. diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md index 73aca3a6f..d7c94d8a0 100644 --- a/docs/installation/openbsd_fi.md +++ b/docs/installation/openbsd_fi.md @@ -18,7 +18,7 @@ Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua Asenna tarvittava ohjelmisto: -`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick` +`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick libvips` #### Optional software From 7d5ef8173735015c473fbc292a7f4b23d3e504a1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 27 Oct 2024 21:52:42 -0400 Subject: [PATCH 47/69] Fix /api/v2/media returning the wrong status code for media processed synchronously The API should return a 202 only if data cannot be returned yet and a followup GET /api/v1/media/:id should be called to retrieve it. This is something Mastodon does when it needs to transcode large media files. It does not apply to Pleroma and causes apps to waste an API call when posting a status which causes apps to appear to hang on higher latency environments, such as on mobile networks. https://docs.joinmastodon.org/methods/media/#v2 --- changelog.d/mediav2_status.fix | 1 + lib/pleroma/web/api_spec/operations/media_operation.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/media_controller.ex | 4 +--- .../web/mastodon_api/controllers/media_controller_test.exs | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 changelog.d/mediav2_status.fix diff --git a/changelog.d/mediav2_status.fix b/changelog.d/mediav2_status.fix new file mode 100644 index 000000000..28e93e030 --- /dev/null +++ b/changelog.d/mediav2_status.fix @@ -0,0 +1 @@ +Fix /api/v2/media returning the wrong status code (202) for media processed synchronously diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex index e6df21246..588b42e06 100644 --- a/lib/pleroma/web/api_spec/operations/media_operation.ex +++ b/lib/pleroma/web/api_spec/operations/media_operation.ex @@ -121,7 +121,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do security: [%{"oAuth" => ["write:media"]}], requestBody: Helpers.request_body("Parameters", create_request()), responses: %{ - 202 => Operation.response("Media", "application/json", Attachment), + 200 => Operation.response("Media", "application/json", Attachment), 400 => Operation.response("Media", "application/json", ApiError), 422 => Operation.response("Media", "application/json", ApiError), 500 => Operation.response("Media", "application/json", ApiError) diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 056bad844..41056d389 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -53,9 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do ) do attachment_data = Map.put(object.data, "id", object.id) - conn - |> put_status(202) - |> render("attachment.json", %{attachment: attachment_data}) + render(conn, "attachment.json", %{attachment: attachment_data}) end end diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index 4adbaa640..3f696d94d 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do conn |> put_req_header("content-type", "multipart/form-data") |> post("/api/v2/media", %{"file" => image, "description" => desc}) - |> json_response_and_validate_schema(202) + |> json_response_and_validate_schema(200) assert media_id = response["id"] @@ -111,7 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do "file" => large_binary, "description" => desc }) - |> json_response_and_validate_schema(202) + |> json_response_and_validate_schema(200) assert media_id = response["id"] From d2de251c4d018c7d517d399d7d5e0e20d853972f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 29 Oct 2024 16:00:18 -0400 Subject: [PATCH 48/69] Pleroma.Upload.Filter.Dedupe: sharding directory structure Dedupe now uses a three-level sharding directory structure to improve performance when many files are uploaded and stored on a filesystem instead of an object store. (note: Minio still affected as it still uses a traditional filesystem) This does not help if you already have hundreds of thousands of files uploaded. The media URLs are permanently part of the activity so the files cannot be relocated. A motivated user could write a tool to move the files and perhaps write an Nginx or equivalent redirect to make the files still accessible, but that is beyond the scope of this change. --- changelog.d/dedupe-sharding.change | 1 + lib/pleroma/upload/filter/dedupe.ex | 10 +++++++++- test/pleroma/object_test.exs | 8 ++++---- test/pleroma/upload/filter/dedupe_test.exs | 4 +++- test/pleroma/upload_test.exs | 6 ++++-- 5 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 changelog.d/dedupe-sharding.change diff --git a/changelog.d/dedupe-sharding.change b/changelog.d/dedupe-sharding.change new file mode 100644 index 000000000..2e140d8a2 --- /dev/null +++ b/changelog.d/dedupe-sharding.change @@ -0,0 +1 @@ +Dedupe upload filter now uses a three-level sharding directory structure diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex index ef793d390..7b278d299 100644 --- a/lib/pleroma/upload/filter/dedupe.ex +++ b/lib/pleroma/upload/filter/dedupe.ex @@ -17,8 +17,16 @@ defmodule Pleroma.Upload.Filter.Dedupe do |> Base.encode16(case: :lower) filename = shasum <> "." <> extension - {:ok, :filtered, %Upload{upload | id: shasum, path: filename}} + + {:ok, :filtered, %Upload{upload | id: shasum, path: shard_path(filename)}} end def filter(_), do: {:ok, :noop} + + @spec shard_path(String.t()) :: String.t() + def shard_path( + <> = filename + ) do + Path.join([a, b, c, filename]) + end end diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index b3c528e32..ed5c2b6c8 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -174,8 +174,9 @@ defmodule Pleroma.ObjectTest do filename = Path.basename(href) - assert {:ok, files} = File.ls(uploads_dir) - assert filename in files + expected_path = Path.join([uploads_dir, Pleroma.Upload.Filter.Dedupe.shard_path(filename)]) + + assert File.exists?(expected_path) Object.delete(note) @@ -183,8 +184,7 @@ defmodule Pleroma.ObjectTest do assert Object.get_by_id(note.id).data["deleted"] assert Object.get_by_id(attachment.id) == nil - assert {:ok, files} = File.ls(uploads_dir) - refute filename in files + refute File.exists?(expected_path) end test "with objects that have legacy data.url attribute" do diff --git a/test/pleroma/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs index 29c181509..cd5ce121b 100644 --- a/test/pleroma/upload/filter/dedupe_test.exs +++ b/test/pleroma/upload/filter/dedupe_test.exs @@ -23,10 +23,12 @@ defmodule Pleroma.Upload.Filter.DedupeTest do tempfile: Path.absname("test/fixtures/image_tmp.jpg") } + expected_path = Dedupe.shard_path(@shasum <> ".jpg") + assert { :ok, :filtered, - %Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"} + %Pleroma.Upload{id: @shasum, path: ^expected_path} } = Dedupe.filter(upload) end end diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs index facb634c3..5fd62fa43 100644 --- a/test/pleroma/upload_test.exs +++ b/test/pleroma/upload_test.exs @@ -149,6 +149,9 @@ defmodule Pleroma.UploadTest do test "copies the file to the configured folder with deduping" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") + expected_filename = "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" + + expected_path = Pleroma.Upload.Filter.Dedupe.shard_path(expected_filename) file = %Plug.Upload{ content_type: "image/jpeg", @@ -159,8 +162,7 @@ defmodule Pleroma.UploadTest do {:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe]) assert List.first(data["url"])["href"] == - Pleroma.Upload.base_url() <> - "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" + Path.join([Pleroma.Upload.base_url(), expected_path]) end test "copies the file to the configured folder without deduping" do From ebea518c8c9dea17ff18c8fa8192a7957adcfadb Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 12 Nov 2024 12:43:16 +0400 Subject: [PATCH 49/69] B DedupeTest: Add explicit test for the sharding structure --- test/pleroma/upload/filter/dedupe_test.exs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/pleroma/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs index cd5ce121b..4dc28b998 100644 --- a/test/pleroma/upload/filter/dedupe_test.exs +++ b/test/pleroma/upload/filter/dedupe_test.exs @@ -10,6 +10,10 @@ defmodule Pleroma.Upload.Filter.DedupeTest do @shasum "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781" + test "generates a shard path for a shasum" do + assert "e3/03/97/" <> _path = Dedupe.shard_path(@shasum) + end + test "adds shasum" do File.cp!( "test/fixtures/image.jpg", From 1e9edccab8d2546427f4e02666955740f6e74a60 Mon Sep 17 00:00:00 2001 From: Codimp Date: Sat, 12 Oct 2024 20:46:07 +0000 Subject: [PATCH 50/69] Translated using Weblate (French) Currently translated at 5.4% (53 of 974 strings) Translation: Pleroma/Pleroma Backend (domain config_descriptions) Translate-URL: https://translate.pleroma.social/projects/pleroma/pleroma-backend-domain-config_descriptions/fr/ --- .../fr/LC_MESSAGES/config_descriptions.po | 128 ++++++++++++------ 1 file changed, 85 insertions(+), 43 deletions(-) diff --git a/priv/gettext/fr/LC_MESSAGES/config_descriptions.po b/priv/gettext/fr/LC_MESSAGES/config_descriptions.po index e43db68aa..c24ab6751 100644 --- a/priv/gettext/fr/LC_MESSAGES/config_descriptions.po +++ b/priv/gettext/fr/LC_MESSAGES/config_descriptions.po @@ -3,14 +3,16 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-22 02:09+0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2024-10-13 21:03+0000\n" +"Last-Translator: Codimp \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Translate Toolkit 3.7.2\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.13.1\n" ## This file is a PO Template file. ## @@ -21,7 +23,6 @@ msgstr "" ## Run "mix gettext.extract" to bring this file up to ## date. Leave "msgstr"s empty as changing them here has no ## effect: edit them in PO (.po) files instead. - #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :esshd" @@ -32,25 +33,30 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config description at :logger" msgid "Logger-related settings" -msgstr "" +msgstr "Paramètres liés à la journalisation" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :mime" msgid "Mime Types settings" -msgstr "" +msgstr "Paramètres des types Mime" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma" msgid "Allows setting a token that can be used to authenticate requests with admin privileges without a normal user account token. Append the `admin_token` parameter to requests to utilize it. (Please reconsider using HTTP Basic Auth or OAuth-based authentication if possible)" msgstr "" +"Permet de configurer un jeton qui peut être utilisé pour authentifier les " +"requêtes avec des privilèges administrateurs sans utiliser un jeton de " +"compte utilisateur standard. Pour l'utiliser, ajoutez le paramètre " +"`admin_token`aux requêtes. (Vous devriez utiliser l'authentification HTTP " +"Basic ou OAuth à la place si vous le pouvez)" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma" msgid "Authenticator" -msgstr "" +msgstr "Authentifieur" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -62,7 +68,7 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config label at :cors_plug" msgid "CORS plug config" -msgstr "" +msgstr "Configuration du plug CORS" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -74,25 +80,25 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config label at :logger" msgid "Logger" -msgstr "" +msgstr "Journaliseur" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config label at :mime" msgid "Mime Types" -msgstr "" +msgstr "Types Mime" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config label at :pleroma" msgid "Pleroma Admin Token" -msgstr "" +msgstr "Jeton Administrateur Pleroma" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config label at :pleroma" msgid "Pleroma Authenticator" -msgstr "" +msgstr "Authentifieur Pleroma" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -104,103 +110,111 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config description at :logger-:console" msgid "Console logger settings" -msgstr "" +msgstr "Paramètres de journalisation de la console" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :logger-:ex_syslogger" msgid "ExSyslogger-related settings" -msgstr "" +msgstr "Paramètres liés à ExSyslogger" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:activitypub" msgid "ActivityPub-related settings" -msgstr "" +msgstr "Paramètres liés à ActivityPub" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:assets" msgid "This section configures assets to be used with various frontends. Currently the only option relates to mascots on the mastodon frontend" msgstr "" +"Cette section configure les annexes (assets) à utiliser avec divers " +"frontaux. La seule option est actuellement liée au mascottes du frontal " +"mastodon" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:auth" msgid "Authentication / authorization settings" -msgstr "" +msgstr "Paramètres d'authentification/autorisations" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:connections_pool" msgid "Advanced settings for `Gun` connections pool" -msgstr "" +msgstr "Paramètres avancés pour le bac (pool) de connexions `Gun`" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:email_notifications" msgid "Email notifications settings" -msgstr "" +msgstr "Paramètres de notification par email" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:features" msgid "Customizable features" -msgstr "" +msgstr "Fonctionnalités personnalisables" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:feed" msgid "Configure feed rendering" -msgstr "" +msgstr "Configurer le rendu des flux" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:frontend_configurations" msgid "This form can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for pleroma_fe are configured. If you want to add your own configuration your settings all fields must be complete." msgstr "" +"Ce formulaire peut être utilisé pour configurer une liste de clés (keyword) " +"qui contiennent les données de configuration pour tout types de frontaux. " +"Par défaut, les paramètres pour pleroma_fe sont configurés. Si vous voulez " +"ajouter vos propres paramètres de configurations, tout les champs doivent " +"être remplis." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:frontends" msgid "Installed frontends management" -msgstr "" +msgstr "Gestion des frontaux installés" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:gopher" msgid "Gopher settings" -msgstr "" +msgstr "Paramètres Gopher" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:hackney_pools" msgid "Advanced settings for `Hackney` connections pools" -msgstr "" +msgstr "Paramètres avancés pour les bacs (pool) de connexions `Hackney`" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:http" msgid "HTTP settings" -msgstr "" +msgstr "Paramètres HTTP" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:http_security" msgid "HTTP security settings" -msgstr "" +msgstr "Paramètres de sécurité HTTP" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:instance" msgid "Instance-related settings" -msgstr "" +msgstr "Paramètres liés à l'instance" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:instances_favicons" msgid "Control favicons for instances" -msgstr "" +msgstr "Gère les favicons des instances" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -212,151 +226,177 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:majic_pool" msgid "Majic/libmagic configuration" -msgstr "" +msgstr "Configuration de majic/libmagic" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:manifest" msgid "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE." msgstr "" +"Cette section décrit les valeurs spécifique à l'instance du manifeste PWA. " +"Actuellement, cette option ne concerne que MastoFE." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:media_preview_proxy" msgid "Media preview proxy" -msgstr "" +msgstr "Proxy de prévisualisation média" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:media_proxy" msgid "Media proxy" -msgstr "" +msgstr "Proxy média" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:modules" msgid "Custom Runtime Modules" -msgstr "" +msgstr "Modules Runtime Personalisés" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf" msgid "General MRF settings" -msgstr "" +msgstr "Paramètres généraux MRF" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_activity_expiration" msgid "Adds automatic expiration to all local activities" -msgstr "" +msgstr "Ajoute une expiration automatique à toutes les activités locales" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_follow_bot" msgid "Automatically follows newly discovered accounts." -msgstr "" +msgstr "Suivre automatiquement les comptes venant d'être découverts." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_hashtag" msgid "Reject, TWKN-remove or Set-Sensitive messsages with specific hashtags (without the leading #)\n\nNote: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.\n" msgstr "" +"Rejeter, Enlever de TWKN ou marquer comme contenu sensible les messages avec " +"des mots-croisillons (sans mettre le # du début)\n" +"\n" +"Note: cette politique MRF est toujours activée. Si vous voulez la " +"désactiver, vous devez configurer des listes vides.\n" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_hellthread" msgid "Block messages with excessive user mentions" -msgstr "" +msgstr "Bloquer les messages avec un nombre excessif de mentions" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_keyword" msgid "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html)." msgstr "" +"Rejeter ou remplacer les mots des messages qui correspondent à un mot clef " +"ou à une [expression rationnelle (Regex)](https://hexdocs.pm/elixir/Regex." +"html)." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_mention" msgid "Block messages which mention a specific user" -msgstr "" +msgstr "Bloquer les messages mentionnant un utilisateur particulier" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_normalize_markup" msgid "MRF NormalizeMarkup settings. Scrub configured hypertext markup." msgstr "" +"Paramètres de normalisation MRF. Balaie les balises hypertextes configurées." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_object_age" msgid "Rejects or delists posts based on their timestamp deviance from your server's clock." msgstr "" +"Rejette ou retire des listes les messages selon l'écart entre leur heure et " +"l'horloge de votre serveur." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_rejectnonpublic" msgid "RejectNonPublic drops posts with non-public visibility settings." msgstr "" +"RejectNonPublic enlève les messages avec des paramètres de visibilité non-" +"publics." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_simple" msgid "Simple ingress policies" -msgstr "" +msgstr "Politiques simples pour entrants" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_steal_emoji" msgid "Steals emojis from selected instances when it sees them." -msgstr "" +msgstr "Vole les emojis des instances sélectionnées quand il les voit." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_subchain" msgid "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." msgstr "" +"Cette politique traite les messages à travers un tuyau séparé lorsqu'un " +"message donné correspond à certain critères. Chaque critère est configuré " +"comme une correspondance entre une expression rationnelle et une liste de " +"modules de politiques." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:mrf_vocabulary" msgid "Filter messages which belong to certain activity vocabularies" msgstr "" +"Filtrer les messages qui correspondent à certain vocabulaires d'activités" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:oauth2" msgid "Configure OAuth 2 provider capabilities" -msgstr "" +msgstr "Configurer les capacités du fournisseur OAuth 2" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:pools" msgid "Advanced settings for `Gun` workers pools" -msgstr "" +msgstr "Paramètres avancés pour les bacs (pools) de travailleurs `Gun`" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:populate_hashtags_table" msgid "`populate_hashtags_table` background migration settings" -msgstr "" +msgstr "Paramètres de migration en arrière-plan `populate_hashtags_table`" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:rate_limit" msgid "Rate limit settings. This is an advanced feature enabled only for :authentication by default." msgstr "" +"Paramètres de limites par secondes. C'est une fonctionnalité avancée qui, " +"par défaut, n'est activée que pour :authentication." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:restrict_unauthenticated" msgid "Disallow viewing timelines, user profiles and statuses for unauthenticated users." msgstr "" +"Empêche de regarder les flux, les profils utilisateurs et les status pour " +"les utilisateurs non-authentifiés." #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:rich_media" msgid "If enabled the instance will parse metadata from attached links to generate link previews" msgstr "" +"Si activé, l'instance interprétera les métadonnées des liens joins pour " +"générer les prévisualisations de liens" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -369,6 +409,8 @@ msgstr "" msgctxt "config description at :pleroma-:static_fe" msgid "Render profiles and posts using server-generated HTML that is viewable without using JavaScript" msgstr "" +"Rendre les profils et les status en utilisant du HTML généré par le serveur " +"qui ne nécessitera pas de JavaScript" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format @@ -380,7 +422,7 @@ msgstr "" #, elixir-autogen, elixir-format msgctxt "config description at :pleroma-:uri_schemes" msgid "URI schemes related settings" -msgstr "" +msgstr "Paramètres liés au schémas d'URI" #: lib/pleroma/docs/translator.ex:5 #, elixir-autogen, elixir-format From 5b3e4cf49bfc80579c6349dd9f81001142a7d3d0 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 12 Nov 2024 14:22:02 +0400 Subject: [PATCH 51/69] B Providers/ActivityPub: Ensure that nothing explodes on unexpected input. --- lib/pleroma/web/metadata/providers/activity_pub.ex | 3 +++ test/pleroma/web/metadata/providers/activity_pub_test.exs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lib/pleroma/web/metadata/providers/activity_pub.ex b/lib/pleroma/web/metadata/providers/activity_pub.ex index 1759a5a0d..bd9f92332 100644 --- a/lib/pleroma/web/metadata/providers/activity_pub.ex +++ b/lib/pleroma/web/metadata/providers/activity_pub.ex @@ -16,4 +16,7 @@ defmodule Pleroma.Web.Metadata.Providers.ActivityPub do def build_tags(%{user: user}) do [{:link, [rel: "alternate", type: "application/activity+json", href: user.ap_id], []}] end + + @impl Provider + def build_tags(_), do: [] end diff --git a/test/pleroma/web/metadata/providers/activity_pub_test.exs b/test/pleroma/web/metadata/providers/activity_pub_test.exs index c379ec092..c5cf78a60 100644 --- a/test/pleroma/web/metadata/providers/activity_pub_test.exs +++ b/test/pleroma/web/metadata/providers/activity_pub_test.exs @@ -31,4 +31,10 @@ defmodule Pleroma.Web.Metadata.Providers.ActivityPubTest do [rel: "alternate", type: "application/activity+json", href: object.data["id"]], []} ] == result end + + test "it returns an empty array for anything else" do + result = ActivityPub.build_tags(%{}) + + assert result == [] + end end From 29b048d351fb9867f11892315bed49adfbb282fb Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 12 Nov 2024 14:35:02 +0400 Subject: [PATCH 52/69] B TwitterAPI/ControllerTest: Actually test the keys --- test/pleroma/web/twitter_api/controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs index 0019b51af..494be9ec7 100644 --- a/test/pleroma/web/twitter_api/controller_test.exs +++ b/test/pleroma/web/twitter_api/controller_test.exs @@ -69,7 +69,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do |> hd() |> Map.keys() - assert keys -- ["id", "app_name", "valid_until", "scopes"] == [] + assert Enum.sort(keys) == Enum.sort(["id", "app_name", "valid_until", "scopes"]) end test "revoke token", %{token: token} do From 0c3b71e1cc9a60941e434f4fad8d6968c1f00b48 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 13 Nov 2024 04:24:05 +0100 Subject: [PATCH 53/69] mix.lock: bump fast_html to 2.3.0 --- changelog.d/bump-lexbor.change | 1 + mix.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/bump-lexbor.change diff --git a/changelog.d/bump-lexbor.change b/changelog.d/bump-lexbor.change new file mode 100644 index 000000000..2c7061a81 --- /dev/null +++ b/changelog.d/bump-lexbor.change @@ -0,0 +1 @@ +- Bumped `fast_html` to v2.3.0, which notably allows to use system-installed lexbor with passing `WITH_SYSTEM_LEXBOR=1` environment variable at build-time \ No newline at end of file diff --git a/mix.lock b/mix.lock index 421f99ec0..a98867142 100644 --- a/mix.lock +++ b/mix.lock @@ -50,7 +50,7 @@ "ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"}, "exile": {:hex, :exile, "0.10.0", "b69e2d27a9af670b0f0a0898addca0eda78f6f5ba95ccfbc9bc6ccdd04925436", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "c62ee8fee565b5ac4a898d0dcd58d2b04fb5eec1655af1ddcc9eb582c6732c33"}, "expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"}, - "fast_html": {:hex, :fast_html, "2.2.0", "6c5ef1be087a4ed613b0379c13f815c4d11742b36b67bb52cee7859847c84520", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "064c4f23b4a6168f9187dac8984b056f2c531bb0787f559fd6a8b34b38aefbae"}, + "fast_html": {:hex, :fast_html, "2.3.0", "08c1d8ead840dd3060ba02c761bed9f37f456a1ddfe30bcdcfee8f651cec06a6", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "f18e3c7668f82d3ae0b15f48d48feeb257e28aa5ab1b0dbf781c7312e5da029d"}, "fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, From c9f9ec04c8b7c24b47acb3f5a00f596b699c2c82 Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 02:13:10 +0300 Subject: [PATCH 54/69] Meilisearch: use PUT method for indexing Mix task See https://github.com/meilisearch/meilisearch/issues/2619 --- lib/mix/tasks/pleroma/search/meilisearch.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mix/tasks/pleroma/search/meilisearch.ex b/lib/mix/tasks/pleroma/search/meilisearch.ex index 8379a0c25..389b8a564 100644 --- a/lib/mix/tasks/pleroma/search/meilisearch.ex +++ b/lib/mix/tasks/pleroma/search/meilisearch.ex @@ -28,7 +28,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do end {:ok, _} = - meili_post( + meili_put( "/indexes/objects/settings/ranking-rules", [ "published:desc", @@ -42,7 +42,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do ) {:ok, _} = - meili_post( + meili_put( "/indexes/objects/settings/searchable-attributes", [ "content" From d65f768b59649de5ed5e76d7dd8248c76fd81a9f Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 02:14:55 +0300 Subject: [PATCH 55/69] Meilisearch: stop attempting to index posts with nil date --- lib/pleroma/search/meilisearch.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/search/meilisearch.ex b/lib/pleroma/search/meilisearch.ex index 9bba5b30f..cafae8099 100644 --- a/lib/pleroma/search/meilisearch.ex +++ b/lib/pleroma/search/meilisearch.ex @@ -122,6 +122,7 @@ defmodule Pleroma.Search.Meilisearch do # Only index public or unlisted Notes if not is_nil(object) and object.data["type"] == "Note" and not is_nil(object.data["content"]) and + not is_nil(object.data["published"]) and (Pleroma.Constants.as_public() in object.data["to"] or Pleroma.Constants.as_public() in object.data["cc"]) and object.data["content"] not in ["", "."] do From 3a82a51a6e8b25c2e58e75329e12a090ad977519 Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 02:16:36 +0300 Subject: [PATCH 56/69] Docs: fix OTP mix task command for Meilisearch --- docs/configuration/search.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/configuration/search.md b/docs/configuration/search.md index d34f84d4f..de1a72203 100644 --- a/docs/configuration/search.md +++ b/docs/configuration/search.md @@ -73,7 +73,7 @@ you have to get the _private key_, which is actually used for authentication. === "OTP" ```sh - ./bin/pleroma_ctl search.meilisearch show-keys + ./bin/pleroma_ctl meilisearch show-keys ``` === "From Source" @@ -103,7 +103,7 @@ To start the initial indexing, run the `index` command: === "OTP" ```sh - ./bin/pleroma_ctl search.meilisearch index + ./bin/pleroma_ctl meilisearch index ``` === "From Source" @@ -118,7 +118,7 @@ of indexing and how many posts have actually been indexed, use the `stats` comma === "OTP" ```sh - ./bin/pleroma_ctl search.meilisearch stats + ./bin/pleroma_ctl meilisearch stats ``` === "From Source" @@ -133,7 +133,7 @@ use the `clear` command: === "OTP" ```sh - ./bin/pleroma_ctl search.meilisearch clear + ./bin/pleroma_ctl meilisearch clear ``` === "From Source" From af7de4c17a0f146a3048d5b2742a292e69bbbb70 Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 02:17:53 +0300 Subject: [PATCH 57/69] Changelog --- changelog.d/meilisearch-misc-fixes.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/meilisearch-misc-fixes.fix diff --git a/changelog.d/meilisearch-misc-fixes.fix b/changelog.d/meilisearch-misc-fixes.fix new file mode 100644 index 000000000..0f127d3a8 --- /dev/null +++ b/changelog.d/meilisearch-misc-fixes.fix @@ -0,0 +1 @@ +Miscellaneous fixes for Meilisearch support From da7132caba49777c25413efc8adc90d27576b07f Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 02:40:27 +0300 Subject: [PATCH 58/69] Remove unused import --- lib/mix/tasks/pleroma/search/meilisearch.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/search/meilisearch.ex b/lib/mix/tasks/pleroma/search/meilisearch.ex index 389b8a564..edce9e871 100644 --- a/lib/mix/tasks/pleroma/search/meilisearch.ex +++ b/lib/mix/tasks/pleroma/search/meilisearch.ex @@ -9,7 +9,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do import Ecto.Query import Pleroma.Search.Meilisearch, - only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete: 1] + only: [meili_put: 2, meili_get: 1, meili_delete: 1] def run(["index"]) do start_pleroma() From 551534f3eeae0062a06e10a322ba17a6d4ee8b9a Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 21 Nov 2024 16:07:09 +0400 Subject: [PATCH 59/69] B ReleaseTasks: Fix task module finding. --- changelog.d/module-search-in-pleroma-ctl.fix | 1 + lib/pleroma/release_tasks.ex | 23 +++++++++++++------- test/pleroma/release_tasks_test.exs | 19 ++++++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 changelog.d/module-search-in-pleroma-ctl.fix create mode 100644 test/pleroma/release_tasks_test.exs diff --git a/changelog.d/module-search-in-pleroma-ctl.fix b/changelog.d/module-search-in-pleroma-ctl.fix new file mode 100644 index 000000000..d32fe3f33 --- /dev/null +++ b/changelog.d/module-search-in-pleroma-ctl.fix @@ -0,0 +1 @@ +Fix pleroma_ctl mix task calls sometimes not being found diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex index bcfcd1243..af2d35c8f 100644 --- a/lib/pleroma/release_tasks.ex +++ b/lib/pleroma/release_tasks.ex @@ -16,17 +16,24 @@ defmodule Pleroma.ReleaseTasks do end end + def find_module(task) do + module_name = + task + |> String.split(".") + |> Enum.map(&String.capitalize/1) + |> then(fn x -> [Mix, Tasks, Pleroma] ++ x end) + |> Module.concat() + + case Code.ensure_loaded(module_name) do + {:module, _} -> module_name + _ -> nil + end + end + defp mix_task(task, args) do Application.load(:pleroma) - {:ok, modules} = :application.get_key(:pleroma, :modules) - module = - Enum.find(modules, fn module -> - module = Module.split(module) - - match?(["Mix", "Tasks", "Pleroma" | _], module) and - String.downcase(List.last(module)) == task - end) + module = find_module(task) if module do module.run(args) diff --git a/test/pleroma/release_tasks_test.exs b/test/pleroma/release_tasks_test.exs new file mode 100644 index 000000000..5a4293189 --- /dev/null +++ b/test/pleroma/release_tasks_test.exs @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ReleaseTaskTest do + use Pleroma.DataCase, async: true + + alias Pleroma.ReleaseTasks + + test "finding the module" do + task = "search.meilisearch" + assert Mix.Tasks.Pleroma.Search.Meilisearch == ReleaseTasks.find_module(task) + + task = "user" + assert Mix.Tasks.Pleroma.User == ReleaseTasks.find_module(task) + + refute ReleaseTasks.find_module("doesnt.exist") + end +end From 14dbf789b3e0e84f588999954f07a378a6ccfcf6 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 21 Nov 2024 16:32:05 +0400 Subject: [PATCH 60/69] Linting --- test/pleroma/{release_tasks_test.exs => release_task_test.exs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/pleroma/{release_tasks_test.exs => release_task_test.exs} (100%) diff --git a/test/pleroma/release_tasks_test.exs b/test/pleroma/release_task_test.exs similarity index 100% rename from test/pleroma/release_tasks_test.exs rename to test/pleroma/release_task_test.exs From 462a6a2000d10e3b6047bc72143ff239caf40186 Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 21 Nov 2024 16:52:30 +0300 Subject: [PATCH 61/69] Revert "Docs: fix OTP mix task command for Meilisearch" This reverts commit 3a82a51a6e8b25c2e58e75329e12a090ad977519. --- docs/configuration/search.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/configuration/search.md b/docs/configuration/search.md index de1a72203..d34f84d4f 100644 --- a/docs/configuration/search.md +++ b/docs/configuration/search.md @@ -73,7 +73,7 @@ you have to get the _private key_, which is actually used for authentication. === "OTP" ```sh - ./bin/pleroma_ctl meilisearch show-keys + ./bin/pleroma_ctl search.meilisearch show-keys ``` === "From Source" @@ -103,7 +103,7 @@ To start the initial indexing, run the `index` command: === "OTP" ```sh - ./bin/pleroma_ctl meilisearch index + ./bin/pleroma_ctl search.meilisearch index ``` === "From Source" @@ -118,7 +118,7 @@ of indexing and how many posts have actually been indexed, use the `stats` comma === "OTP" ```sh - ./bin/pleroma_ctl meilisearch stats + ./bin/pleroma_ctl search.meilisearch stats ``` === "From Source" @@ -133,7 +133,7 @@ use the `clear` command: === "OTP" ```sh - ./bin/pleroma_ctl meilisearch clear + ./bin/pleroma_ctl search.meilisearch clear ``` === "From Source" From ced6b10c70769e39ee7d3b6a3fe63b8c2aea3ec0 Mon Sep 17 00:00:00 2001 From: feld Date: Mon, 12 Aug 2024 19:52:37 +0000 Subject: [PATCH 62/69] Merge branch 'swoosh-mailgun' into 'develop' Fix Swoosh Mailgun support See merge request pleroma/pleroma!4217 --- changelog.d/mailgun.fix | 1 + mix.exs | 1 + mix.lock | 1 + 3 files changed, 3 insertions(+) create mode 100644 changelog.d/mailgun.fix diff --git a/changelog.d/mailgun.fix b/changelog.d/mailgun.fix new file mode 100644 index 000000000..855588752 --- /dev/null +++ b/changelog.d/mailgun.fix @@ -0,0 +1 @@ +The Swoosh email adapter for Mailgun was missing a new dependency on :multipart diff --git a/mix.exs b/mix.exs index 69e52e526..e3c8559ba 100644 --- a/mix.exs +++ b/mix.exs @@ -202,6 +202,7 @@ defmodule Pleroma.Mixfile do {:bandit, "~> 1.5.2"}, {:websock_adapter, "~> 0.5.6"}, {:oban_live_dashboard, "~> 0.1.1"}, + {:multipart, "~> 0.4.0", optional: true}, ## dev & test {:phoenix_live_reload, "~> 1.3.3", only: :dev}, diff --git a/mix.lock b/mix.lock index 61ede9e5e..37ac1768b 100644 --- a/mix.lock +++ b/mix.lock @@ -84,6 +84,7 @@ "mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"}, "mogrify": {:hex, :mogrify, "0.8.0", "3506f3ca3f7b95a155f3b4ef803b5db176f5a0633723e3fe85e0d6399e3b11c8", [:mix], [], "hexpm", "2278d245f07056ea3b586e98801e933695147066fa4cf563f552c1b4f0ff8ad9"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, + "multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, From 2977779e94cb34610d0c2369ecacf7d721ebc5ab Mon Sep 17 00:00:00 2001 From: feld Date: Fri, 6 Sep 2024 16:30:07 +0000 Subject: [PATCH 63/69] Merge branch 'well-known' into 'develop' NodeInfo: Accept application/activity+json requests See merge request pleroma/pleroma!4242 --- changelog.d/well-known.change | 1 + lib/pleroma/web/router.ex | 2 +- test/pleroma/web/node_info_test.exs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changelog.d/well-known.change diff --git a/changelog.d/well-known.change b/changelog.d/well-known.change new file mode 100644 index 000000000..e928124fb --- /dev/null +++ b/changelog.d/well-known.change @@ -0,0 +1 @@ +Accept application/activity+json for requests to .well-known/nodeinfo diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index fc40a1143..4294dffa5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -189,7 +189,7 @@ defmodule Pleroma.Web.Router do end pipeline :well_known do - plug(:accepts, ["json", "jrd", "jrd+json", "xml", "xrd+xml"]) + plug(:accepts, ["activity+json", "json", "jrd", "jrd+json", "xml", "xrd+xml"]) end pipeline :config do diff --git a/test/pleroma/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs index f474220be..afe4ebb36 100644 --- a/test/pleroma/web/node_info_test.exs +++ b/test/pleroma/web/node_info_test.exs @@ -24,6 +24,19 @@ defmodule Pleroma.Web.NodeInfoTest do |> get(href) |> json_response(200) end) + + accept_types = [ + "application/activity+json", + "application/json", + "application/jrd+json" + ] + + for type <- accept_types do + conn + |> put_req_header("accept", type) + |> get("/.well-known/nodeinfo") + |> json_response(200) + end end test "nodeinfo shows staff accounts", %{conn: conn} do From a6e97c497b5ac418d9825200542d4d4d273f91f7 Mon Sep 17 00:00:00 2001 From: feld Date: Fri, 6 Sep 2024 13:27:06 +0000 Subject: [PATCH 64/69] Merge branch 'following-state-bug' into 'develop' Fix Following status bug See merge request pleroma/pleroma!4251 --- changelog.d/following-state.fix | 1 + .../web/mastodon_api/views/account_view.ex | 19 +++++---- mix.lock | 2 +- .../mastodon_api/views/account_view_test.exs | 39 +++++++++++++++++++ 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 changelog.d/following-state.fix diff --git a/changelog.d/following-state.fix b/changelog.d/following-state.fix new file mode 100644 index 000000000..314ea6210 --- /dev/null +++ b/changelog.d/following-state.fix @@ -0,0 +1 @@ +Resolved edge case where the API can report you are following a user but the relationship is not fully established. diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 6976ca6e5..298c73986 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -92,14 +92,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do User.get_follow_state(reading_user, target) end - followed_by = - if following_relationships do - case FollowingRelationship.find(following_relationships, target, reading_user) do - %{state: :follow_accept} -> true - _ -> false - end - else - User.following?(target, reading_user) + followed_by = FollowingRelationship.following?(target, reading_user) + following = FollowingRelationship.following?(reading_user, target) + + requested = + cond do + following -> false + true -> match?(:follow_pending, follow_state) end subscribing = @@ -114,7 +113,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags %{ id: to_string(target.id), - following: follow_state == :follow_accept, + following: following, followed_by: followed_by, blocking: UserRelationship.exists?( @@ -150,7 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do ), subscribing: subscribing, notifying: subscribing, - requested: follow_state == :follow_pending, + requested: requested, domain_blocking: User.blocks_domain?(reading_user, target), showing_reblogs: not UserRelationship.exists?( diff --git a/mix.lock b/mix.lock index 37ac1768b..09db91ffe 100644 --- a/mix.lock +++ b/mix.lock @@ -22,7 +22,7 @@ "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, - "credo": {:hex, :credo, "1.7.3", "05bb11eaf2f2b8db370ecaa6a6bda2ec49b2acd5e0418bc106b73b07128c0436", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535"}, + "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index f0711fa0d..73ec67a3d 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -456,6 +456,45 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test_relationship_rendering(user, other_user, expected) end + test "relationship does not indicate following if a FollowingRelationship is missing" do + user = insert(:user) + other_user = insert(:user, local: false) + + # Create a follow relationship with the real Follow Activity and Accept it + assert {:ok, _, _, _} = CommonAPI.follow(other_user, user) + assert {:ok, _} = CommonAPI.accept_follow_request(user, other_user) + + assert %{data: %{"state" => "accept"}} = + Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, other_user) + + # Fetch the relationship and forcibly delete it to simulate + # a Follow Accept that did not complete processing + %{following_relationships: [relationship]} = + Pleroma.UserRelationship.view_relationships_option(user, [other_user]) + + assert {:ok, _} = Pleroma.Repo.delete(relationship) + + assert %{following_relationships: [], user_relationships: []} == + Pleroma.UserRelationship.view_relationships_option(user, [other_user]) + + expected = + Map.merge( + @blank_response, + %{ + following: false, + followed_by: false, + muting: false, + muting_notifications: false, + subscribing: false, + notifying: false, + showing_reblogs: true, + id: to_string(other_user.id) + } + ) + + test_relationship_rendering(user, other_user, expected) + end + test "represent a relationship for the blocking and blocked user" do user = insert(:user) other_user = insert(:user) From f45f17b5ff4e0bc454d9340c500e8b923c9e57cb Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 8 Aug 2024 05:29:46 +0000 Subject: [PATCH 65/69] Merge branch 'follow-validator' into 'develop' Do not require a cc field when validating an incoming Follow activity See merge request pleroma/pleroma!4212 --- changelog.d/follow-validator.fix | 1 + .../object_validators/accept_reject_validator.ex | 2 +- .../activity_pub/object_validators/block_validator.ex | 2 +- .../activity_pub/object_validators/follow_validator.ex | 2 +- .../object_validators/follow_validation_test.exs | 10 ++++++++++ 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 changelog.d/follow-validator.fix diff --git a/changelog.d/follow-validator.fix b/changelog.d/follow-validator.fix new file mode 100644 index 000000000..d49932b7b --- /dev/null +++ b/changelog.d/follow-validator.fix @@ -0,0 +1 @@ +Improve the FollowValidator to successfully incoming activities with an errant cc field. diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex index d611da051..03ab83347 100644 --- a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do defp validate_data(cng) do cng - |> validate_required([:id, :type, :actor, :to, :cc, :object]) + |> validate_required([:id, :type, :actor, :to, :object]) |> validate_inclusion(:type, ["Accept", "Reject"]) |> validate_actor_presence() |> validate_object_presence(allowed_types: ["Follow"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex index 0de87a27e..98340545c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do defp validate_data(cng) do cng - |> validate_required([:id, :type, :actor, :to, :cc, :object]) + |> validate_required([:id, :type, :actor, :to, :object]) |> validate_inclusion(:type, ["Block"]) |> CommonValidations.validate_actor_presence() |> CommonValidations.validate_actor_presence(field_name: :object) diff --git a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex index b3ca5b691..e4e97bf72 100644 --- a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator do defp validate_data(cng) do cng - |> validate_required([:id, :type, :actor, :to, :cc, :object]) + |> validate_required([:id, :type, :actor, :to, :object]) |> validate_inclusion(:type, ["Follow"]) |> validate_inclusion(:state, ~w{pending reject accept}) |> validate_actor_presence() diff --git a/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs index 371368e0e..acf6e8d8f 100644 --- a/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs @@ -22,5 +22,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidationTest do test "validates a basic follow object", %{valid_follow: valid_follow} do assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) end + + test "supports a nil cc", %{valid_follow: valid_follow} do + valid_follow_with_nil_cc = Map.put(valid_follow, "cc", nil) + assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow_with_nil_cc, []) + end + + test "supports an empty cc", %{valid_follow: valid_follow} do + valid_follow_with_empty_cc = Map.put(valid_follow, "cc", []) + assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow_with_empty_cc, []) + end end end From 53c2d2cd87d35094b0ae623b8daab4d303ed5fcb Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 13 Nov 2024 08:22:44 +0000 Subject: [PATCH 66/69] Merge branch 'mastodon-websocket-fix' into 'develop' Fix Mastodon WebSocket authentication See merge request pleroma/pleroma!4206 --- changelog.d/mastodon-websocket.fix | 1 + lib/pleroma/web/endpoint.ex | 1 + lib/pleroma/web/mastodon_api/websocket_handler.ex | 11 ++++++++++- mix.exs | 3 ++- mix.lock | 2 +- test/pleroma/integration/mastodon_websocket_test.exs | 11 +++++++++++ 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 changelog.d/mastodon-websocket.fix diff --git a/changelog.d/mastodon-websocket.fix b/changelog.d/mastodon-websocket.fix new file mode 100644 index 000000000..2c4fe86ef --- /dev/null +++ b/changelog.d/mastodon-websocket.fix @@ -0,0 +1 @@ +Fix Mastodon WebSocket authentication diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index fef907ace..bab3c9fd0 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.Endpoint do websocket: [ path: "/", compress: false, + connect_info: [:sec_websocket_protocol], error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []}, fullsweep_after: 20 ] diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 730295a4c..3ed1cdd6c 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do # This only prepares the connection and is not in the process yet @impl Phoenix.Socket.Transport def connect(%{params: params} = transport_info) do - with access_token <- Map.get(params, "access_token"), + with access_token <- find_access_token(transport_info), {:ok, user, oauth_token} <- authenticate_request(access_token), {:ok, topic} <- Streamer.get_topic(params["stream"], user, oauth_token, params) do @@ -244,4 +244,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do def handle_error(conn, _reason) do Plug.Conn.send_resp(conn, 404, "Not Found") end + + defp find_access_token(%{ + connect_info: %{sec_websocket_protocol: [token]} + }), + do: token + + defp find_access_token(%{params: %{"access_token" => token}}), do: token + + defp find_access_token(_), do: nil end diff --git a/mix.exs b/mix.exs index e3c8559ba..88140c69b 100644 --- a/mix.exs +++ b/mix.exs @@ -132,7 +132,8 @@ defmodule Pleroma.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.7.3"}, + {:phoenix, + git: "https://github.com/feld/phoenix", branch: "v1.7.14-websocket-headers", override: true}, {:phoenix_ecto, "~> 4.4"}, {:ecto_sql, "~> 3.10"}, {:ecto_enum, "~> 1.4"}, diff --git a/mix.lock b/mix.lock index 09db91ffe..4d0b9003c 100644 --- a/mix.lock +++ b/mix.lock @@ -95,7 +95,7 @@ "open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, - "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, + "phoenix": {:git, "https://github.com/feld/phoenix", "fb6dc76c657422e49600896c64aab4253fceaef6", [branch: "v1.7.14-websocket-headers"]}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"}, "phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"}, diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index f499f54ad..88f32762d 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -268,6 +268,17 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do end) end + test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do + assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) + + capture_log(fn -> + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + + Process.sleep(30) + end) + end + test "accepts valid token on client-sent event", %{token: token} do assert {:ok, pid} = start_socket() From 6a0883e5d33bd03fb3d71ad3a3560adc5ce6d626 Mon Sep 17 00:00:00 2001 From: feld Date: Fri, 16 Aug 2024 00:37:10 +0000 Subject: [PATCH 67/69] Merge branch 'bugfix-truncate-remote-user-fields' into 'develop' User: truncate remote user fields instead of rejecting See merge request pleroma/pleroma!4220 --- .../bugfix-truncate-remote-user-fields.fix | 1 + lib/pleroma/user.ex | 2 ++ test/pleroma/user_test.exs | 15 +++++++++++++++ .../transmogrifier/user_update_handling_test.exs | 4 ++-- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 changelog.d/bugfix-truncate-remote-user-fields.fix diff --git a/changelog.d/bugfix-truncate-remote-user-fields.fix b/changelog.d/bugfix-truncate-remote-user-fields.fix new file mode 100644 index 000000000..239a3c224 --- /dev/null +++ b/changelog.d/bugfix-truncate-remote-user-fields.fix @@ -0,0 +1 @@ +Truncate remote user fields, avoids them getting rejected diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e28d76a7c..4f6cdc03e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -463,6 +463,7 @@ defmodule Pleroma.User do def remote_user_changeset(struct \\ %User{local: false}, params) do bio_limit = Config.get([:instance, :user_bio_length], 5000) name_limit = Config.get([:instance, :user_name_length], 100) + fields_limit = Config.get([:instance, :max_remote_account_fields], 0) name = case params[:name] do @@ -476,6 +477,7 @@ defmodule Pleroma.User do |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now()) |> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:bio, bio_limit) + |> Map.update(:fields, [], &Enum.take(&1, fields_limit)) |> truncate_fields_param() |> fix_follower_address() diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 036ae78fb..06afc0709 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -1075,6 +1075,21 @@ defmodule Pleroma.UserTest do refute cs.valid? end + + test "it truncates fields" do + clear_config([:instance, :max_remote_account_fields], 2) + + fields = [ + %{"name" => "One", "value" => "Uno"}, + %{"name" => "Two", "value" => "Dos"}, + %{"name" => "Three", "value" => "Tres"} + ] + + cs = User.remote_user_changeset(@valid_remote |> Map.put(:fields, fields)) + + assert [%{"name" => "One", "value" => "Uno"}, %{"name" => "Two", "value" => "Dos"}] == + Ecto.Changeset.get_field(cs, :fields) + end end describe "followers and friends" do diff --git a/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs index da46f063a..851c60850 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -119,8 +119,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do user = User.get_cached_by_ap_id(user.ap_id) assert user.fields == [ - %{"name" => "foo", "value" => "updated"}, - %{"name" => "foo1", "value" => "updated"} + %{"name" => "foo", "value" => "bar"}, + %{"name" => "foo11", "value" => "bar11"} ] update_data = From 7bb2dccc058cd1659ab21942b06657abf9127365 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 26 Nov 2024 14:09:09 +0100 Subject: [PATCH 68/69] Version 2.7.1 --- CHANGELOG.md | 12 ++++++++++++ changelog.d/bugfix-truncate-remote-user-fields.fix | 1 - changelog.d/follow-validator.fix | 1 - changelog.d/following-state.fix | 1 - changelog.d/mailgun.fix | 1 - changelog.d/mastodon-websocket.fix | 1 - changelog.d/well-known.change | 1 - mix.exs | 2 +- 8 files changed, 13 insertions(+), 7 deletions(-) delete mode 100644 changelog.d/bugfix-truncate-remote-user-fields.fix delete mode 100644 changelog.d/follow-validator.fix delete mode 100644 changelog.d/following-state.fix delete mode 100644 changelog.d/mailgun.fix delete mode 100644 changelog.d/mastodon-websocket.fix delete mode 100644 changelog.d/well-known.change diff --git a/CHANGELOG.md b/CHANGELOG.md index 61bb2ab54..424a9afbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.7.1 + +### Changed +- Accept `application/activity+json` for requests to `/.well-known/nodeinfo` + +### Fixed +- Truncate remote user fields, avoids them getting rejected +- Improve the `FollowValidator` to successfully incoming activities with an errant `cc` field. +- Resolved edge case where the API can report you are following a user but the relationship is not fully established. +- The Swoosh email adapter for Mailgun was missing a new dependency on `:multipart` +- Fix Mastodon WebSocket authentication + ## 2.7.0 ### Security diff --git a/changelog.d/bugfix-truncate-remote-user-fields.fix b/changelog.d/bugfix-truncate-remote-user-fields.fix deleted file mode 100644 index 239a3c224..000000000 --- a/changelog.d/bugfix-truncate-remote-user-fields.fix +++ /dev/null @@ -1 +0,0 @@ -Truncate remote user fields, avoids them getting rejected diff --git a/changelog.d/follow-validator.fix b/changelog.d/follow-validator.fix deleted file mode 100644 index d49932b7b..000000000 --- a/changelog.d/follow-validator.fix +++ /dev/null @@ -1 +0,0 @@ -Improve the FollowValidator to successfully incoming activities with an errant cc field. diff --git a/changelog.d/following-state.fix b/changelog.d/following-state.fix deleted file mode 100644 index 314ea6210..000000000 --- a/changelog.d/following-state.fix +++ /dev/null @@ -1 +0,0 @@ -Resolved edge case where the API can report you are following a user but the relationship is not fully established. diff --git a/changelog.d/mailgun.fix b/changelog.d/mailgun.fix deleted file mode 100644 index 855588752..000000000 --- a/changelog.d/mailgun.fix +++ /dev/null @@ -1 +0,0 @@ -The Swoosh email adapter for Mailgun was missing a new dependency on :multipart diff --git a/changelog.d/mastodon-websocket.fix b/changelog.d/mastodon-websocket.fix deleted file mode 100644 index 2c4fe86ef..000000000 --- a/changelog.d/mastodon-websocket.fix +++ /dev/null @@ -1 +0,0 @@ -Fix Mastodon WebSocket authentication diff --git a/changelog.d/well-known.change b/changelog.d/well-known.change deleted file mode 100644 index e928124fb..000000000 --- a/changelog.d/well-known.change +++ /dev/null @@ -1 +0,0 @@ -Accept application/activity+json for requests to .well-known/nodeinfo diff --git a/mix.exs b/mix.exs index 88140c69b..2448b0c66 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.7.0"), + version: version("2.7.1"), elixir: "~> 1.13", elixirc_paths: elixirc_paths(Mix.env()), compilers: Mix.compilers(), From 3f98c8bd1b86a87e204879da4172178173050638 Mon Sep 17 00:00:00 2001 From: kPherox Date: Wed, 27 Nov 2024 15:45:14 +0900 Subject: [PATCH 69/69] fix: skip directory entries In OTP 27.1 or later, `:zip.unzip/2` without `:skip_directories` option returns directory entries. However in OTP 26, passing `:skip_directories` returns a `:bad_option` error, so this option is not available for compatibility. --- lib/pleroma/frontend.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex index 816499917..a4f427ae5 100644 --- a/lib/pleroma/frontend.ex +++ b/lib/pleroma/frontend.ex @@ -74,11 +74,14 @@ defmodule Pleroma.Frontend do new_file_path = Path.join(dest, path) - new_file_path + path |> Path.dirname() + |> then(&Path.join(dest, &1)) |> File.mkdir_p!() - File.write!(new_file_path, data) + if not File.dir?(new_file_path) do + File.write!(new_file_path, data) + end end) end end