From 718e8e1edb537aca984216be39b3be5c8af4e6da Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 16 May 2021 21:39:58 -0500 Subject: Create NsfwApiPolicy --- .../web/activity_pub/mrf/nsfw_api_policy.ex | 185 +++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex new file mode 100644 index 000000000..9ad175b1b --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -0,0 +1,185 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do + @moduledoc """ + Hide, delete, or mark sensitive NSFW content with artificial intelligence. + + Requires a NSFW API server, configured like so: + + config :pleroma, Pleroma.Web.ActivityPub.MRF.NsfwMRF, + url: "http://127.0.0.1:5000/", + threshold: 0.8, + mark_sensitive: true, + unlist: false, + reject: false + + The NSFW API server must implement an HTTP endpoint like this: + + curl http://localhost:5000/?url=https://fedi.com/images/001.jpg + + Returning a response like this: + + {"score", 0.314} + + Where a score is 0-1, with `1` being definitely NSFW. + + A good API server is here: https://github.com/EugenCepoi/nsfw_api + You can run it with Docker with a one-liner: + + docker run -it -p 127.0.0.1:5000:5000/tcp --env PORT=5000 eugencepoi/nsfw_api:latest + + Options: + + - `url`: Base URL of the API server. Default: "http://127.0.0.1:5000/" + - `threshold`: Lowest score to take action on. Default: `0.7` + - `mark_sensitive`: Mark sensitive all detected NSFW content? Default: `true` + - `unlist`: Unlist all detected NSFW content? Default: `false` + - `reject`: Reject all detected NSFW content (takes precedence)? Default: `false` + """ + alias Pleroma.Config + alias Pleroma.Constants + alias Pleroma.HTTP + alias Pleroma.User + + require Logger + require Pleroma.Constants + + @behaviour Pleroma.Web.ActivityPub.MRF + @policy :mrf_nsfw_api + + defp build_request_url(url) do + Config.get([@policy, :url]) + |> URI.parse() + |> Map.put(:query, "url=#{url}") + |> URI.to_string() + end + + defp parse_url(url) do + request = build_request_url(url) + + with {:ok, %Tesla.Env{body: body}} <- HTTP.get(request) do + Jason.decode(body) + else + error -> + Logger.warn(""" + [NsfwApiPolicy]: The API server failed. Skipping. + #{inspect(error)} + """) + + error + end + end + + defp check_url_nsfw(url) when is_binary(url) do + threshold = Config.get([@policy, :threshold]) + + case parse_url(url) do + {:ok, %{"score" => score}} when score >= threshold -> + {:nsfw, %{url: url, score: score, threshold: threshold}} + + _ -> + {:sfw, url} + end + end + + defp check_url_nsfw(%{"href" => url}) when is_binary(url) do + check_url_nsfw(url) + end + + defp check_attachment_nsfw(%{"url" => urls} = attachment) when is_list(urls) do + if Enum.all?(urls, &match?({:sfw, _}, check_url_nsfw(&1))) do + {:sfw, attachment} + else + {:nsfw, attachment} + end + end + + defp check_object_nsfw(%{"attachment" => attachments} = object) when is_list(attachments) do + if Enum.all?(attachments, &match?({:sfw, _}, check_attachment_nsfw(&1))) do + {:sfw, object} + else + {:nsfw, object} + end + end + + defp check_object_nsfw(%{"object" => %{} = child_object} = object) do + case check_object_nsfw(child_object) do + {:sfw, _} -> {:sfw, object} + {:nsfw, _} -> {:nsfw, object} + end + end + + defp check_object_nsfw(object), do: {:sfw, object} + + @impl true + def filter(object) do + with {:sfw, object} <- check_object_nsfw(object) do + {:ok, object} + else + {:nsfw, _data} -> handle_nsfw(object) + _ -> {:reject, "NSFW: Attachment rejected"} + end + end + + defp handle_nsfw(object) do + if Config.get([@policy, :reject]) do + {:reject, object} + else + {:ok, + object + |> maybe_unlist() + |> maybe_mark_sensitive()} + end + end + + defp maybe_unlist(object) do + if Config.get([@policy, :unlist]) do + unlist(object) + else + object + end + end + + defp maybe_mark_sensitive(object) do + if Config.get([@policy, :mark_sensitive]) do + mark_sensitive(object) + else + object + end + end + + defp unlist(%{"to" => to, "cc" => cc, "actor" => actor} = object) do + with %User{} = user <- User.get_cached_by_ap_id(actor) do + to = + [user.follower_address | to] + |> List.delete(Constants.as_public()) + |> Enum.uniq() + + cc = + [Constants.as_public() | cc] + |> List.delete(user.follower_address) + |> Enum.uniq() + + object + |> Map.put("to", to) + |> Map.put("cc", cc) + end + end + + defp mark_sensitive(%{"object" => child_object} = object) when is_map(child_object) do + Map.put(object, "object", mark_sensitive(child_object)) + end + + defp mark_sensitive(object) when is_map(object) do + tags = (object["tag"] || []) ++ ["nsfw"] + + object + |> Map.put("tag", tags) + |> Map.put("sensitive", true) + end + + @impl true + def describe, do: {:ok, %{}} +end -- cgit v1.2.3 From f15d419062b5f9aba2a2e84257dc2379b44f92e8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 16 Jun 2021 22:30:18 -0500 Subject: NsfwApiPolicy: raise if can't fetch user --- lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index 9ad175b1b..63e6af0a0 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -165,6 +165,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do object |> Map.put("to", to) |> Map.put("cc", cc) + else + _ -> raise "[NsfwApiPolicy]: Could not fetch user #{actor}" end end -- cgit v1.2.3 From 2b3dfbb42f7ec0c5604876276a81d55a05955416 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 17 Jun 2021 14:36:51 -0500 Subject: NsfwApiPolicy: add tests --- .../web/activity_pub/mrf/nsfw_api_policy.ex | 45 +++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index 63e6af0a0..9dcdf560e 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -49,14 +49,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do @behaviour Pleroma.Web.ActivityPub.MRF @policy :mrf_nsfw_api - defp build_request_url(url) do + def build_request_url(url) do Config.get([@policy, :url]) |> URI.parse() + |> fix_path() |> Map.put(:query, "url=#{url}") |> URI.to_string() end - defp parse_url(url) do + def parse_url(url) do request = build_request_url(url) with {:ok, %Tesla.Env{body: body}} <- HTTP.get(request) do @@ -72,23 +73,26 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do end end - defp check_url_nsfw(url) when is_binary(url) do + def check_url_nsfw(url) when is_binary(url) do threshold = Config.get([@policy, :threshold]) case parse_url(url) do {:ok, %{"score" => score}} when score >= threshold -> {:nsfw, %{url: url, score: score, threshold: threshold}} + {:ok, %{"score" => score}} -> + {:sfw, %{url: url, score: score, threshold: threshold}} + _ -> - {:sfw, url} + {:sfw, %{url: url, score: nil, threshold: threshold}} end end - defp check_url_nsfw(%{"href" => url}) when is_binary(url) do + def check_url_nsfw(%{"href" => url}) when is_binary(url) do check_url_nsfw(url) end - defp check_attachment_nsfw(%{"url" => urls} = attachment) when is_list(urls) do + def check_attachment_nsfw(%{"url" => urls} = attachment) when is_list(urls) do if Enum.all?(urls, &match?({:sfw, _}, check_url_nsfw(&1))) do {:sfw, attachment} else @@ -96,7 +100,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do end end - defp check_object_nsfw(%{"attachment" => attachments} = object) when is_list(attachments) do + def check_attachment_nsfw(%{"url" => url} = attachment) when is_binary(url) do + case check_url_nsfw(url) do + {:sfw, _} -> {:sfw, attachment} + {:nsfw, _} -> {:nsfw, attachment} + end + end + + def check_object_nsfw(%{"attachment" => attachments} = object) when is_list(attachments) do if Enum.all?(attachments, &match?({:sfw, _}, check_attachment_nsfw(&1))) do {:sfw, object} else @@ -104,14 +115,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do end end - defp check_object_nsfw(%{"object" => %{} = child_object} = object) do + def check_object_nsfw(%{"object" => %{} = child_object} = object) do case check_object_nsfw(child_object) do {:sfw, _} -> {:sfw, object} {:nsfw, _} -> {:nsfw, object} end end - defp check_object_nsfw(object), do: {:sfw, object} + def check_object_nsfw(object), do: {:sfw, object} @impl true def filter(object) do @@ -150,7 +161,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do end end - defp unlist(%{"to" => to, "cc" => cc, "actor" => actor} = object) do + def unlist(%{"to" => to, "cc" => cc, "actor" => actor} = object) do with %User{} = user <- User.get_cached_by_ap_id(actor) do to = [user.follower_address | to] @@ -166,15 +177,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do |> Map.put("to", to) |> Map.put("cc", cc) else - _ -> raise "[NsfwApiPolicy]: Could not fetch user #{actor}" + _ -> raise "[NsfwApiPolicy]: Could not find user #{actor}" end end - defp mark_sensitive(%{"object" => child_object} = object) when is_map(child_object) do + def mark_sensitive(%{"object" => child_object} = object) when is_map(child_object) do Map.put(object, "object", mark_sensitive(child_object)) end - defp mark_sensitive(object) when is_map(object) do + def mark_sensitive(object) when is_map(object) do tags = (object["tag"] || []) ++ ["nsfw"] object @@ -182,6 +193,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do |> Map.put("sensitive", true) end + # Hackney needs a trailing slash + defp fix_path(%URI{path: path} = uri) when is_binary(path) do + path = String.trim_trailing(path, "/") <> "/" + Map.put(uri, :path, path) + end + + defp fix_path(%URI{path: nil} = uri), do: Map.put(uri, :path, "/") + @impl true def describe, do: {:ok, %{}} end -- cgit v1.2.3 From b293c14a1b01398029dfa80aea306946efc2f284 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 17 Jun 2021 14:52:07 -0500 Subject: NsfwApiPolicy: add describe/0 and config_description/0 --- .../web/activity_pub/mrf/nsfw_api_policy.ex | 56 +++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index 9dcdf560e..a1560c584 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do config :pleroma, Pleroma.Web.ActivityPub.MRF.NsfwMRF, url: "http://127.0.0.1:5000/", - threshold: 0.8, + threshold: 0.7, mark_sensitive: true, unlist: false, reject: false @@ -202,5 +202,57 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do defp fix_path(%URI{path: nil} = uri), do: Map.put(uri, :path, "/") @impl true - def describe, do: {:ok, %{}} + def describe do + options = %{ + threshold: Config.get([@policy, :threshold]), + mark_sensitive: Config.get([@policy, :mark_sensitive]), + unlist: Config.get([@policy, :unlist]), + reject: Config.get([@policy, :reject]) + } + + {:ok, %{@policy => options}} + end + + @impl true + def config_description do + %{ + key: @policy, + related_policy: to_string(__MODULE__), + label: "NSFW API Policy", + description: + "Hide, delete, or mark sensitive NSFW content with artificial intelligence. Requires running an external API server.", + children: [ + %{ + key: :url, + type: :string, + description: "Base URL of the API server.", + suggestions: ["http://127.0.0.1:5000/"] + }, + %{ + key: :threshold, + type: :float, + description: "Lowest score to take action on. Between 0 and 1.", + suggestions: [0.7] + }, + %{ + key: :mark_sensitive, + type: :boolean, + description: "Mark sensitive all detected NSFW content?", + suggestions: [true] + }, + %{ + key: :unlist, + type: :boolean, + description: "Unlist sensitive all detected NSFW content?", + suggestions: [false] + }, + %{ + key: :reject, + type: :boolean, + description: "Reject sensitive all detected NSFW content (takes precedence)?", + suggestions: [false] + } + ] + } + end end -- cgit v1.2.3 From a704d5499c03cb5609ea38a5f2ef06095ced3ef3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 17 Jun 2021 15:32:42 -0500 Subject: NsfwApiPolicy: Fall back more generously when functions don't match --- lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index a1560c584..920821f38 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -92,6 +92,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do check_url_nsfw(url) end + def check_url_nsfw(url) do + threshold = Config.get([@policy, :threshold]) + {:sfw, %{url: url, score: nil, threshold: threshold}} + end + def check_attachment_nsfw(%{"url" => urls} = attachment) when is_list(urls) do if Enum.all?(urls, &match?({:sfw, _}, check_url_nsfw(&1))) do {:sfw, attachment} @@ -107,6 +112,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do end end + def check_attachment_nsfw(attachment), do: {:sfw, attachment} + def check_object_nsfw(%{"attachment" => attachments} = object) when is_list(attachments) do if Enum.all?(attachments, &match?({:sfw, _}, check_attachment_nsfw(&1))) do {:sfw, object} -- cgit v1.2.3 From 9423052e9217aa1358950d37c5c96b11d554b37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 25 Apr 2022 12:39:36 +0200 Subject: Add "status" notification type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/notification.ex | 38 +++++++++++++++++++--- .../api_spec/operations/notification_operation.ex | 4 ++- .../controllers/notification_controller.ex | 1 + .../web/mastodon_api/views/notification_view.ex | 3 ++ lib/pleroma/web/push/impl.ex | 1 + 5 files changed, 41 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 52fd2656b..d142baa8b 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -73,6 +73,7 @@ defmodule Pleroma.Notification do pleroma:report reblog poll + status } def changeset(%Notification{} = notification, attrs) do @@ -397,11 +398,18 @@ defmodule Pleroma.Notification do {enabled_receivers, disabled_receivers} = get_notified_from_activity(activity) potential_receivers = enabled_receivers ++ disabled_receivers + {enabled_subscribers, disabled_subscribers} = get_notified_subscribers_from_activity(activity) + potential_subscribers = (enabled_subscribers ++ disabled_subscribers) -- potential_receivers + notifications = - Enum.map(potential_receivers, fn user -> - do_send = do_send && user in enabled_receivers - create_notification(activity, user, do_send: do_send) - end) + (Enum.map(potential_receivers, fn user -> + do_send = do_send && user in enabled_receivers + create_notification(activity, user, do_send: do_send) + end) ++ + Enum.map(potential_subscribers, fn user -> + do_send = do_send && user in enabled_subscribers + create_notification(activity, user, do_send: do_send, type: "status") + end)) |> Enum.reject(&is_nil/1) {:ok, notifications} @@ -533,6 +541,27 @@ defmodule Pleroma.Notification do def get_notified_from_activity(_, _local_only), do: {[], []} + def get_notified_subscribers_from_activity(activity, local_only \\ true) + + def get_notified_subscribers_from_activity( + %Activity{data: %{"type" => "Create"}} = activity, + local_only + ) do + notification_enabled_ap_ids = + [] + |> Utils.maybe_notify_subscribers(activity) + + potential_receivers = + User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only) + + notification_enabled_users = + Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end) + + {notification_enabled_users, potential_receivers -- notification_enabled_users} + end + + def get_notified_subscribers_from_activity(_, _), do: {[], []} + # For some activities, only notify the author of the object def get_potential_receiver_ap_ids(%{data: %{"type" => type, "object" => object_id}}) when type in ~w{Like Announce EmojiReact} do @@ -557,7 +586,6 @@ defmodule Pleroma.Notification do [] |> Utils.maybe_notify_to_recipients(activity) |> Utils.maybe_notify_mentioned_recipients(activity) - |> Utils.maybe_notify_subscribers(activity) |> Utils.maybe_notify_followers(activity) |> Enum.uniq() end diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index 7f2336ff6..aa965fabb 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -196,7 +196,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do "pleroma:report", "move", "follow_request", - "poll" + "poll", + "status" ], description: """ The type of event that resulted in the notification. @@ -210,6 +211,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do - `pleroma:emoji_reaction` - Someone reacted with emoji to your status - `pleroma:chat_mention` - Someone mentioned you in a chat message - `pleroma:report` - Someone was reported + - `status` - Someone you are subscribed to created a status """ } end diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 932bc6423..9209e8ebd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -51,6 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do move pleroma:emoji_reaction poll + status } def index(%{assigns: %{user: user}} = conn, params) do params = diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 0dc7f3beb..b10b0893c 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -103,6 +103,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do "mention" -> put_status(response, activity, reading_user, status_render_opts) + "status" -> + put_status(response, activity, reading_user, status_render_opts) + "favourite" -> put_status(response, parent_activity_fn.(), reading_user, status_render_opts) diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index daf3eeb9e..77bc2941d 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -183,6 +183,7 @@ defmodule Pleroma.Web.Push.Impl do def format_title(%{type: type}, mastodon_type) do case mastodon_type || type do "mention" -> "New Mention" + "status" -> "New Status" "follow" -> "New Follower" "follow_request" -> "New Follow Request" "reblog" -> "New Repeat" -- cgit v1.2.3 From fa2a6d5d6b24657ddbda4ef11d2e6dbcb59545d3 Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Thu, 7 Apr 2022 18:25:02 +0200 Subject: feat: simple, but not stupid, uploader for IPFS fix: format fix with credo --- lib/pleroma/upload.ex | 13 +++++++-- lib/pleroma/uploaders/ipfs.ex | 64 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/uploaders/ipfs.ex (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index db2909276..de39bcd6c 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -235,8 +235,14 @@ defmodule Pleroma.Upload do "" end - [base_url, path] - |> Path.join() + uploader = Config.get([Pleroma.Upload, :uploader]) + + if uploader == Pleroma.Uploaders.IPFS && String.contains?(base_url, "{CID}") do + String.replace(base_url, "{CID}", path) + else + [base_url, path] + |> Path.join() + end end defp url_from_spec(_upload, _base_url, {:url, url}), do: url @@ -273,6 +279,9 @@ defmodule Pleroma.Upload do Path.join([upload_base_url, bucket_with_namespace]) end + Pleroma.Uploaders.IPFS -> + Config.get([Pleroma.Uploaders.IPFS, :get_gateway_url]) + _ -> public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/" end diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex new file mode 100644 index 000000000..b46e9322e --- /dev/null +++ b/lib/pleroma/uploaders/ipfs.ex @@ -0,0 +1,64 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Uploaders.IPFS do + @behaviour Pleroma.Uploaders.Uploader + require Logger + + alias Pleroma.Config + alias Tesla.Multipart + + @impl true + def get_file(file) do + b_url = Pleroma.Upload.base_url() + + if String.contains?(b_url, "{CID}") do + {:ok, {:url, String.replace(b_url, "{CID}", URI.decode(file))}} + else + {:error, "IPFS Get URL doesn't contain '{CID}' placeholder"} + end + end + + @impl true + def put_file(%Pleroma.Upload{} = upload) do + config = Config.get([__MODULE__]) + post_base_url = Keyword.get(config, :post_gateway_url) + + mp = + Multipart.new() + |> Multipart.add_content_type_param("charset=utf-8") + |> Multipart.add_file(upload.tempfile) + + final_url = Path.join([post_base_url, "/api/v0/add"]) + + case Pleroma.HTTP.post(final_url, mp, [], params: ["cid-version": "1"]) do + {:ok, ret} -> + case Jason.decode(ret.body) do + {:ok, ret} -> + {:ok, {:file, ret["Hash"]}} + + error -> + Logger.error("#{__MODULE__}: #{inspect(error)}") + {:error, "JSON decode failed"} + end + + error -> + Logger.error("#{__MODULE__}: #{inspect(error)}") + {:error, "IPFS Gateway Upload failed"} + end + end + + @impl true + def delete_file(file) do + config = Config.get([__MODULE__]) + post_base_url = Keyword.get(config, :post_gateway_url) + + final_url = Path.join([post_base_url, "/api/v0/files/rm"]) + + case Pleroma.HTTP.post(final_url, "", [], params: [arg: file]) do + {:ok, %{status_code: 204}} -> :ok + error -> {:error, inspect(error)} + end + end +end -- cgit v1.2.3 From 43dfa58ebda407a0813d398bee8d0ae3e5c9fd5b Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Mon, 11 Apr 2022 15:10:01 +0200 Subject: added tests for ipfs uploader. adapted changelog.md accordingly. improved ipfs uploader with external suggestions fix lint description.exs --- lib/pleroma/upload.ex | 6 ++---- lib/pleroma/uploaders/ipfs.ex | 14 +++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index de39bcd6c..b51d23f9e 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -235,10 +235,8 @@ defmodule Pleroma.Upload do "" end - uploader = Config.get([Pleroma.Upload, :uploader]) - - if uploader == Pleroma.Uploaders.IPFS && String.contains?(base_url, "{CID}") do - String.replace(base_url, "{CID}", path) + if String.contains?(base_url, "<%= cid %>") do + EEx.eval_string(base_url, cid: path) else [base_url, path] |> Path.join() diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index b46e9322e..722c68fa1 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -13,10 +13,10 @@ defmodule Pleroma.Uploaders.IPFS do def get_file(file) do b_url = Pleroma.Upload.base_url() - if String.contains?(b_url, "{CID}") do - {:ok, {:url, String.replace(b_url, "{CID}", URI.decode(file))}} + if String.contains?(b_url, "<%= cid %>") do + {:ok, {:url, EEx.eval_string(b_url, cid: URI.decode(file))}} else - {:error, "IPFS Get URL doesn't contain '{CID}' placeholder"} + {:error, "IPFS Get URL doesn't contain 'cid' placeholder"} end end @@ -36,7 +36,11 @@ defmodule Pleroma.Uploaders.IPFS do {:ok, ret} -> case Jason.decode(ret.body) do {:ok, ret} -> - {:ok, {:file, ret["Hash"]}} + if Map.has_key?(ret, "Hash") do + {:ok, {:file, ret["Hash"]}} + else + {:error, "JSON doesn't contain Hash value"} + end error -> Logger.error("#{__MODULE__}: #{inspect(error)}") @@ -45,7 +49,7 @@ defmodule Pleroma.Uploaders.IPFS do error -> Logger.error("#{__MODULE__}: #{inspect(error)}") - {:error, "IPFS Gateway Upload failed"} + {:error, "IPFS Gateway upload failed"} end end -- cgit v1.2.3 From 44659ecd65fb2251f9130fcecf1732b8931104c1 Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Sat, 16 Apr 2022 09:38:49 +0200 Subject: ipfs: revert to String.replace for cid placeholder ipfs: fix lint --- lib/pleroma/upload.ex | 4 ++-- lib/pleroma/uploaders/ipfs.ex | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index b51d23f9e..8a01cf613 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -235,8 +235,8 @@ defmodule Pleroma.Upload do "" end - if String.contains?(base_url, "<%= cid %>") do - EEx.eval_string(base_url, cid: path) + if String.contains?(base_url, Pleroma.Uploaders.IPFS.placeholder()) do + String.replace(base_url, Pleroma.Uploaders.IPFS.placeholder(), path) else [base_url, path] |> Path.join() diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index 722c68fa1..dde520d8e 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -9,12 +9,15 @@ defmodule Pleroma.Uploaders.IPFS do alias Pleroma.Config alias Tesla.Multipart + @placeholder "{CID}" + def placeholder, do: @placeholder + @impl true def get_file(file) do b_url = Pleroma.Upload.base_url() - if String.contains?(b_url, "<%= cid %>") do - {:ok, {:url, EEx.eval_string(b_url, cid: URI.decode(file))}} + if String.contains?(b_url, @placeholder) do + {:ok, {:url, String.replace(b_url, @placeholder, URI.decode(file))}} else {:error, "IPFS Get URL doesn't contain 'cid' placeholder"} end -- cgit v1.2.3 From 7c1af86f979ecebcd38995e5278fe2d59a36eda5 Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Mon, 9 May 2022 12:15:40 +0200 Subject: ipfs: refactor final_url generation. add tests for final_url fix lint --- lib/pleroma/uploaders/ipfs.ex | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index dde520d8e..7a7481d81 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -12,6 +12,13 @@ defmodule Pleroma.Uploaders.IPFS do @placeholder "{CID}" def placeholder, do: @placeholder + def get_final_url(method) do + config = Config.get([__MODULE__]) + post_base_url = Keyword.get(config, :post_gateway_url) + + Path.join([post_base_url, method]) + end + @impl true def get_file(file) do b_url = Pleroma.Upload.base_url() @@ -25,15 +32,12 @@ defmodule Pleroma.Uploaders.IPFS do @impl true def put_file(%Pleroma.Upload{} = upload) do - config = Config.get([__MODULE__]) - post_base_url = Keyword.get(config, :post_gateway_url) - mp = Multipart.new() |> Multipart.add_content_type_param("charset=utf-8") |> Multipart.add_file(upload.tempfile) - final_url = Path.join([post_base_url, "/api/v0/add"]) + final_url = get_final_url("/api/v0/add") case Pleroma.HTTP.post(final_url, mp, [], params: ["cid-version": "1"]) do {:ok, ret} -> @@ -42,7 +46,7 @@ defmodule Pleroma.Uploaders.IPFS do if Map.has_key?(ret, "Hash") do {:ok, {:file, ret["Hash"]}} else - {:error, "JSON doesn't contain Hash value"} + {:error, "JSON doesn't contain Hash key"} end error -> @@ -58,10 +62,7 @@ defmodule Pleroma.Uploaders.IPFS do @impl true def delete_file(file) do - config = Config.get([__MODULE__]) - post_base_url = Keyword.get(config, :post_gateway_url) - - final_url = Path.join([post_base_url, "/api/v0/files/rm"]) + final_url = get_final_url("/api/v0/files/rm") case Pleroma.HTTP.post(final_url, "", [], params: [arg: file]) do {:ok, %{status_code: 204}} -> :ok -- cgit v1.2.3 From 98f268e5ecc5bab98c98270a582f8b3f0e3be4e8 Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Thu, 9 Jun 2022 19:24:13 +0200 Subject: ipfs: small refactor and more tests --- lib/pleroma/uploaders/ipfs.ex | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index 7a7481d81..9f6f26e2e 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -9,16 +9,24 @@ defmodule Pleroma.Uploaders.IPFS do alias Pleroma.Config alias Tesla.Multipart - @placeholder "{CID}" - def placeholder, do: @placeholder - - def get_final_url(method) do + defp get_final_url(method) do config = Config.get([__MODULE__]) post_base_url = Keyword.get(config, :post_gateway_url) Path.join([post_base_url, method]) end + def put_file_endpoint() do + get_final_url("/api/v0/add") + end + + def delete_file_endpoint() do + get_final_url("/api/v0/files/rm") + end + + @placeholder "{CID}" + def placeholder, do: @placeholder + @impl true def get_file(file) do b_url = Pleroma.Upload.base_url() @@ -37,9 +45,7 @@ defmodule Pleroma.Uploaders.IPFS do |> Multipart.add_content_type_param("charset=utf-8") |> Multipart.add_file(upload.tempfile) - final_url = get_final_url("/api/v0/add") - - case Pleroma.HTTP.post(final_url, mp, [], params: ["cid-version": "1"]) do + case Pleroma.HTTP.post(put_file_endpoint(), mp, [], params: ["cid-version": "1"]) do {:ok, ret} -> case Jason.decode(ret.body) do {:ok, ret} -> @@ -62,9 +68,7 @@ defmodule Pleroma.Uploaders.IPFS do @impl true def delete_file(file) do - final_url = get_final_url("/api/v0/files/rm") - - case Pleroma.HTTP.post(final_url, "", [], params: [arg: file]) do + case Pleroma.HTTP.post(delete_file_endpoint(), "", [], params: [arg: file]) do {:ok, %{status_code: 204}} -> :ok error -> {:error, inspect(error)} end -- cgit v1.2.3 From 254f2ea85400ebd692fc4a45f5ac22fedd49ec09 Mon Sep 17 00:00:00 2001 From: Claudio Maradonna Date: Thu, 9 Jun 2022 23:38:50 +0200 Subject: ipfs: remove unused alias fix analysis job --- lib/pleroma/uploaders/ipfs.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index 9f6f26e2e..32e06c5cf 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -16,11 +16,11 @@ defmodule Pleroma.Uploaders.IPFS do Path.join([post_base_url, method]) end - def put_file_endpoint() do + def put_file_endpoint do get_final_url("/api/v0/add") end - def delete_file_endpoint() do + def delete_file_endpoint do get_final_url("/api/v0/files/rm") end -- cgit v1.2.3 From c899af1d6acad1895240a0247e9b91eca5db08df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 14 Apr 2022 20:09:43 +0200 Subject: Reject requests from specified instances if `authorized_fetch_mode` is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/signature.ex | 16 ++++++--- .../web/mastodon_api/views/instance_view.ex | 7 ++++ lib/pleroma/web/plugs/http_signature_plug.ex | 40 ++++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index dbe6fd209..d5ba5c4fb 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -37,8 +37,7 @@ defmodule Pleroma.Signature do end def fetch_public_key(conn) do - with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), - {:ok, actor_id} <- key_id_to_actor_id(kid), + with {:ok, actor_id} <- get_actor_id(conn), {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} else @@ -48,8 +47,7 @@ defmodule Pleroma.Signature do end def refetch_public_key(conn) do - with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), - {:ok, actor_id} <- key_id_to_actor_id(kid), + with {:ok, actor_id} <- get_actor_id(conn), {:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id), {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} @@ -59,6 +57,16 @@ defmodule Pleroma.Signature do end end + def get_actor_id(conn) do + with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), + {:ok, actor_id} <- key_id_to_actor_id(kid) do + {:ok, actor_id} + else + e -> + {:error, e} + end + end + def sign(%User{} = user, headers) do with {:ok, %{keys: keys}} <- User.ensure_keys_present(user), {:ok, private_key, _} <- Keys.keys_from_pem(keys) do diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 62931bd41..017bd62e2 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -105,6 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do def federation do quarantined = Config.get([:instance, :quarantined_instances], []) + rejected = Config.get([:instance, :rejected_instances], []) if Config.get([:mrf, :transparency]) do {:ok, data} = MRF.describe() @@ -124,6 +125,12 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end) |> Map.new() }) + |> Map.put( + :rejected_instances, + rejected + |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end) + |> Map.new() + ) else %{} end diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index d023754a6..cf80b9b14 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -5,6 +5,10 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do import Plug.Conn import Phoenix.Controller, only: [get_format: 1, text: 2] + + alias Pleroma.Config + alias Pleroma.Web.ActivityPub.MRF + require Logger def init(options) do @@ -19,7 +23,9 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do if get_format(conn) == "activity+json" do conn |> maybe_assign_valid_signature() + |> maybe_assign_actor_id() |> maybe_require_signature() + |> maybe_filter_requests() else conn end @@ -46,6 +52,16 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end end + defp maybe_assign_actor_id(%{assigns: %{valid_signature: true}} = conn) do + adapter = Application.get_env(:http_signatures, :adapter) + + {:ok, actor_id} = adapter.get_actor_id(conn) + + assign(conn, :actor_id, actor_id) + end + + defp maybe_assign_actor_id(conn), do: conn + defp has_signature_header?(conn) do conn |> get_req_header("signature") |> Enum.at(0, false) end @@ -62,4 +78,28 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do conn end end + + defp maybe_filter_requests(%{halted: true} = conn), do: conn + + defp maybe_filter_requests(conn) do + if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do + %{host: host} = URI.parse(conn.assigns.actor_id) + + if MRF.subdomain_match?(rejected_domains(), host) do + conn + |> put_status(:unauthorized) + |> halt() + else + conn + end + else + conn + end + end + + defp rejected_domains do + Config.get([:instance, :rejected_instances]) + |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() + |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() + end end -- cgit v1.2.3 From 2ae1b802f260e9ad8eaa585907d9505545ceb872 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 9 Mar 2023 10:21:11 +0100 Subject: AttachmentValidator: Add support for Honk "summary" + "name" As used by Honk and supported by Mastodon --- lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index 398020bff..766421e60 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do field(:type, :string) field(:mediaType, ObjectValidators.MIME, default: "application/octet-stream") field(:name, :string) + field(:summary, :string) field(:blurhash, :string) embeds_many :url, UrlObjectValidator, primary_key: false do @@ -44,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do |> fix_url() struct - |> cast(data, [:id, :type, :mediaType, :name, :blurhash]) + |> cast(data, [:id, :type, :mediaType, :name, :summary, :blurhash]) |> cast_embed(:url, with: &url_changeset/2, required: true) |> validate_inclusion(:type, ~w[Link Document Audio Image Video]) |> validate_required([:type, :mediaType]) -- cgit v1.2.3 From 197647a04e66c1af3ae691a4507612fdbee9c48c Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 9 Mar 2023 10:35:57 +0100 Subject: MastoAPI Attachment: Use "summary" for descriptions if present --- lib/pleroma/web/api_spec/schemas/attachment.ex | 6 +++++- lib/pleroma/web/mastodon_api/views/status_view.ex | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex index 48634a14f..e89f2ddd0 100644 --- a/lib/pleroma/web/api_spec/schemas/attachment.ex +++ b/lib/pleroma/web/api_spec/schemas/attachment.ex @@ -50,7 +50,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do pleroma: %Schema{ type: :object, properties: %{ - mime_type: %Schema{type: :string, description: "mime type of the attachment"} + mime_type: %Schema{type: :string, description: "mime type of the attachment"}, + name: %Schema{ + type: :string, + description: "Name of the attachment, typically the filename" + } } } }, diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0a8c98b44..7a3af8acb 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -571,6 +571,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do to_string(attachment["id"] || hash_id) end + description = + if attachment["summary"] do + HTML.strip_tags(attachment["summary"]) + else + attachment["name"] + end + + name = if attachment["summary"], do: attachment["name"] + + pleroma = + %{mime_type: media_type} + |> Maps.put_if_present(:name, name) + %{ id: attachment_id, url: href, @@ -578,8 +591,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do preview_url: href_preview, text_url: href, type: type, - description: attachment["name"], - pleroma: %{mime_type: media_type}, + description: description, + pleroma: pleroma, blurhash: attachment["blurhash"] } |> Maps.put_if_present(:meta, meta) -- cgit v1.2.3 From b6a9d87f16a4806eab7a6da874d6f75b65d4f214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 15 Mar 2023 19:44:42 +0100 Subject: Display reposted replies with exclude_replies: true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/activity_pub.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 3979d418e..4b956c680 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -964,8 +964,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_replies(query, %{exclude_replies: true}) do from( - [_activity, object] in query, - where: fragment("?->>'inReplyTo' is null", object.data) + [activity, object] in query, + where: + fragment("?->>'inReplyTo' is null or ?->>'type' = 'Announce'", object.data, activity.data) ) end -- cgit v1.2.3 From f271ea6e432d685c113582e5944d79e12c153016 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 16 Dec 2023 18:22:32 +0100 Subject: Move Plugs.RemoteIP.maybe_add_cidr/1 to InetHelper.parse_cidr/1 --- lib/pleroma/helpers/inet_helper.ex | 11 +++++++++++ lib/pleroma/web/plugs/remote_ip.ex | 14 ++------------ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex index 704d37f8a..3500fc679 100644 --- a/lib/pleroma/helpers/inet_helper.ex +++ b/lib/pleroma/helpers/inet_helper.ex @@ -16,4 +16,15 @@ defmodule Pleroma.Helpers.InetHelper do def parse_address(ip) do :inet.parse_address(ip) end + + def parse_cidr(proxy) when is_binary(proxy) do + proxy = + cond do + "/" in String.codepoints(proxy) -> proxy + InetCidr.v4?(InetCidr.parse_address!(proxy)) -> proxy <> "/32" + InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128" + end + + InetCidr.parse(proxy, true) + end end diff --git a/lib/pleroma/web/plugs/remote_ip.ex b/lib/pleroma/web/plugs/remote_ip.ex index f207d9fef..3a4bffb50 100644 --- a/lib/pleroma/web/plugs/remote_ip.ex +++ b/lib/pleroma/web/plugs/remote_ip.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.Plugs.RemoteIp do """ alias Pleroma.Config + alias Pleroma.Helpers.InetHelper import Plug.Conn @behaviour Plug @@ -30,19 +31,8 @@ defmodule Pleroma.Web.Plugs.RemoteIp do proxies = Config.get([__MODULE__, :proxies], []) |> Enum.concat(reserved) - |> Enum.map(&maybe_add_cidr/1) + |> Enum.map(&InetHelper.parse_cidr/1) {headers, proxies} end - - defp maybe_add_cidr(proxy) when is_binary(proxy) do - proxy = - cond do - "/" in String.codepoints(proxy) -> proxy - InetCidr.v4?(InetCidr.parse_address!(proxy)) -> proxy <> "/32" - InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128" - end - - InetCidr.parse(proxy, true) - end end -- cgit v1.2.3 From 086ba59d0346be870dc7df2660fbb55666bf0af7 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 16 Dec 2023 18:56:46 +0100 Subject: HTTPSignaturePlug: Add :authorized_fetch_mode_exceptions --- lib/pleroma/web/plugs/http_signature_plug.ex | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index e814efc2c..7ec202662 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -3,6 +3,8 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do + alias Pleroma.Helpers.InetHelper + import Plug.Conn import Phoenix.Controller, only: [get_format: 1, text: 2] require Logger @@ -89,12 +91,20 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn - defp maybe_require_signature(conn) do + defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do - conn - |> put_status(:unauthorized) - |> text("Request not signed") - |> halt() + exceptions = + Pleroma.Config.get([:activitypub, :authorized_fetch_mode_exceptions], []) + |> Enum.map(&InetHelper.parse_cidr/1) + + if Enum.any?(exceptions, fn x -> InetCidr.contains?(x, remote_ip) end) do + conn + else + conn + |> put_status(:unauthorized) + |> text("Request not signed") + |> halt() + end else conn end -- cgit v1.2.3 From 7dfd148ff8a4f2d349d6d6f92d788effdaab36f3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 9 Dec 2023 18:32:26 -0500 Subject: Logger metadata for inbound federation requests --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index e38a94966..d2b2cae0b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -52,6 +52,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do when action in [:activity, :object] ) + plug(:log_inbox_metadata when action in [:inbox]) plug(:set_requester_reachable when action in [:inbox]) plug(:relay_active? when action in [:relay]) @@ -521,6 +522,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do conn end + defp log_inbox_metadata(conn = %{params: %{"actor" => actor, "type" => type}}, _) do + Logger.metadata(actor: actor, type: type) + conn + end + + defp log_inbox_metadata(conn, _), do: conn + def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do with {:ok, object} <- ActivityPub.upload( -- cgit v1.2.3 From 40823462e7779fb79a4fcd458daa5e7095a6030b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 17 Dec 2023 18:20:22 -0500 Subject: Logger metadata for request path and authenticated user --- lib/pleroma/web/endpoint.ex | 2 ++ lib/pleroma/web/plugs/logger_metadata_path.ex | 12 ++++++++++++ lib/pleroma/web/plugs/logger_metadata_user.ex | 18 ++++++++++++++++++ lib/pleroma/web/router.ex | 8 ++++++++ 4 files changed, 40 insertions(+) create mode 100644 lib/pleroma/web/plugs/logger_metadata_path.ex create mode 100644 lib/pleroma/web/plugs/logger_metadata_user.ex (limited to 'lib') diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 2e2104904..fef907ace 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -38,6 +38,8 @@ defmodule Pleroma.Web.Endpoint do plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) + plug(Pleroma.Web.Plugs.LoggerMetadataPath) + plug(Pleroma.Web.Plugs.SetLocalePlug) plug(CORSPlug) plug(Pleroma.Web.Plugs.HTTPSecurityPlug) diff --git a/lib/pleroma/web/plugs/logger_metadata_path.ex b/lib/pleroma/web/plugs/logger_metadata_path.ex new file mode 100644 index 000000000..a5553cfc8 --- /dev/null +++ b/lib/pleroma/web/plugs/logger_metadata_path.ex @@ -0,0 +1,12 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.LoggerMetadataPath do + def init(opts), do: opts + + def call(conn, _) do + Logger.metadata(path: conn.request_path) + conn + end +end diff --git a/lib/pleroma/web/plugs/logger_metadata_user.ex b/lib/pleroma/web/plugs/logger_metadata_user.ex new file mode 100644 index 000000000..6a5c0041d --- /dev/null +++ b/lib/pleroma/web/plugs/logger_metadata_user.ex @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.LoggerMetadataUser do + alias Pleroma.User + + def init(opts), do: opts + + def call(%{assigns: %{user: user = %User{}}} = conn, _) do + Logger.metadata(user: user.nickname) + conn + end + + def call(conn, _) do + conn + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 4fe0cb02f..f0414cc35 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -29,6 +29,7 @@ defmodule Pleroma.Web.Router do pipeline :browser do plug(:accepts, ["html"]) plug(:fetch_session) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :oauth do @@ -67,12 +68,14 @@ defmodule Pleroma.Web.Router do plug(:fetch_session) plug(:authenticate) plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :no_auth_or_privacy_expectations_api do plug(:base_api) plug(:after_auth) plug(Pleroma.Web.Plugs.IdempotencyPlug) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end # Pipeline for app-related endpoints (no user auth checks — app-bound tokens must be supported) @@ -83,12 +86,14 @@ defmodule Pleroma.Web.Router do pipeline :api do plug(:expect_public_instance_or_user_authentication) plug(:no_auth_or_privacy_expectations_api) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :authenticated_api do plug(:expect_user_authentication) plug(:no_auth_or_privacy_expectations_api) plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :admin_api do @@ -99,6 +104,7 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Web.Plugs.UserIsStaffPlug) plug(Pleroma.Web.Plugs.IdempotencyPlug) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :require_admin do @@ -179,6 +185,7 @@ defmodule Pleroma.Web.Router do plug(:browser) plug(:authenticate) plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :well_known do @@ -193,6 +200,7 @@ defmodule Pleroma.Web.Router do pipeline :pleroma_api do plug(:accepts, ["html", "json"]) plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) + plug(Pleroma.Web.Plugs.LoggerMetadataUser) end pipeline :mailbox_preview do -- cgit v1.2.3 From 9e6cf45906b9d56834d032d4e0fa436cc5e17031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 6 Apr 2024 11:43:07 +0200 Subject: /api/v1/accounts/familiar_followers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/user.ex | 34 ++++++++++++++++++ .../web/api_spec/operations/account_operation.ex | 42 ++++++++++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 34 +++++++++++++++++- lib/pleroma/web/mastodon_api/views/account_view.ex | 19 ++++++++++ lib/pleroma/web/router.ex | 1 + 5 files changed, 129 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 778e20526..6d6aa98b5 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1404,6 +1404,40 @@ defmodule Pleroma.User do |> Repo.all() end + @spec get_familiar_followers_query(User.t(), User.t(), pos_integer() | nil) :: Ecto.Query.t() + def get_familiar_followers_query(%User{} = user, %User{} = current_user, nil) do + friends = + get_friends_query(current_user) + |> where([u], not u.hide_follows) + |> select([u], u.id) + + User.Query.build(%{is_active: true}) + |> where([u], u.id not in ^[user.id, current_user.id]) + |> join(:inner, [u], r in FollowingRelationship, + as: :followers_relationships, + on: r.following_id == ^user.id and r.follower_id == u.id + ) + |> where([followers_relationships: r], r.state == ^:follow_accept) + |> where([followers_relationships: r], r.follower_id in subquery(friends)) + end + + def get_familiar_followers_query(%User{} = user, %User{} = current_user, page) do + user + |> get_familiar_followers_query(current_user, nil) + |> User.Query.paginate(page, 20) + end + + @spec get_familiar_followers_query(User.t(), User.t()) :: Ecto.Query.t() + def get_familiar_followers_query(%User{} = user, %User{} = current_user), + do: get_familiar_followers_query(user, current_user, nil) + + @spec get_familiar_followers(User.t(), User.t(), pos_integer() | nil) :: {:ok, list(User.t())} + def get_familiar_followers(%User{} = user, %User{} = current_user, page \\ nil) do + user + |> get_familiar_followers_query(current_user, page) + |> Repo.all() + end + def increase_note_count(%User{} = user) do User |> where(id: ^user.id) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 36025e47a..244f18dc7 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.ActorType alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.List alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -513,6 +514,47 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + def familiar_followers_operation do + %Operation{ + tags: ["Retrieve account information"], + summary: "Followers you know", + operationId: "AccountController.relationships", + description: "Returns followers of given account you know.", + security: [%{"oAuth" => ["read:follows"]}], + parameters: [ + Operation.parameter( + :id, + :query, + %Schema{ + oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}] + }, + "Account IDs", + example: "123" + ) + ], + responses: %{ + 200 => + Operation.response("Accounts", "application/json", %Schema{ + title: "ArrayOfAccounts", + type: :array, + items: %Schema{ + title: "Account", + type: :object, + properties: %{ + id: FlakeID, + accounts: %Schema{ + title: "ArrayOfAccounts", + type: :array, + items: Account, + example: [Account.schema().example] + } + } + } + }) + } + } + end + defp create_request do %Schema{ title: "AccountCreateRequest", diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 9226a2deb..47e6f0a64 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -72,7 +72,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do %{scopes: ["follow", "write:blocks"]} when action in [:block, :unblock] ) - plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships) + plug( + OAuthScopesPlug, + %{scopes: ["read:follows"]} when action in [:relationships, :familiar_followers] + ) plug( OAuthScopesPlug, @@ -629,6 +632,35 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do ) end + @doc "GET /api/v1/accounts/familiar_followers" + def familiar_followers( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _id + ) do + users = + User.get_all_by_ids(List.wrap(id)) + |> Enum.map(&%{id: &1.id, accounts: get_familiar_followers(&1, user)}) + + conn + |> render("familiar_followers.json", + for: user, + users: users, + as: :user + ) + end + + defp get_familiar_followers(%{id: id} = user, %{id: id}) do + User.get_familiar_followers(user, user) + end + + defp get_familiar_followers(%{hide_followers: true}, _current_user) do + [] + end + + defp get_familiar_followers(user, current_user) do + User.get_familiar_followers(user, current_user) + end + @doc "GET /api/v1/identity_proofs" def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 267c3e3ed..6976ca6e5 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -193,6 +193,25 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do render_many(targets, AccountView, "relationship.json", render_opts) end + def render("familiar_followers.json", %{users: users} = opts) do + opts = + opts + |> Map.merge(%{as: :user}) + |> Map.delete(:users) + + users + |> render_many(AccountView, "familiar_followers.json", opts) + end + + def render("familiar_followers.json", %{user: %{id: id, accounts: accounts}} = opts) do + accounts = + accounts + |> render_many(AccountView, "show.json", opts) + |> Enum.filter(&Enum.any?/1) + + %{id: id, accounts: accounts} + end + defp do_render("show.json", %{user: user} = opts) do self = opts[:for] == user diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 4fe0cb02f..644f6cc81 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -633,6 +633,7 @@ defmodule Pleroma.Web.Router do patch("/accounts/update_credentials", AccountController, :update_credentials) get("/accounts/relationships", AccountController, :relationships) + get("/accounts/familiar_followers", AccountController, :familiar_followers) get("/accounts/:id/lists", AccountController, :lists) get("/accounts/:id/identity_proofs", AccountController, :identity_proofs) get("/endorsements", AccountController, :endorsements) -- cgit v1.2.3 From 88412daf118f15a40119f0b47a740a442ec5040c Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 12 Apr 2024 09:15:06 +0000 Subject: Apply @lanodan's suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/account_operation.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 244f18dc7..85f02166f 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -517,9 +517,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do def familiar_followers_operation do %Operation{ tags: ["Retrieve account information"], - summary: "Followers you know", - operationId: "AccountController.relationships", - description: "Returns followers of given account you know.", + summary: "Followers that you follow", + operationId: "AccountController.familiar_followers", + description: + "Obtain a list of all accounts that follow a given account, filtered for accounts you follow.", security: [%{"oAuth" => ["read:follows"]}], parameters: [ Operation.parameter( -- cgit v1.2.3 From 1bf3ae07b6719a762310615811a317035175ad2e Mon Sep 17 00:00:00 2001 From: faried nawaz Date: Mon, 18 Sep 2023 02:13:01 +0500 Subject: add options to mix pleroma.database prune_objects to delete more activities --- lib/mix/tasks/pleroma/database.ex | 165 +++++++++++++++++++++++++++++++++----- 1 file changed, 145 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 93ee57dc3..13ac6536c 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -67,43 +67,168 @@ defmodule Mix.Tasks.Pleroma.Database do OptionParser.parse( args, strict: [ - vacuum: :boolean + vacuum: :boolean, + keep_threads: :boolean, + keep_non_public: :boolean, + prune_orphaned_activities: :boolean ] ) start_pleroma() deadline = Pleroma.Config.get([:instance, :remote_post_retention_days]) + time_deadline = NaiveDateTime.utc_now() |> NaiveDateTime.add(-(deadline * 86_400)) - Logger.info("Pruning objects older than #{deadline} days") + log_message = "Pruning objects older than #{deadline} days" - time_deadline = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(-(deadline * 86_400)) + log_message = + if Keyword.get(options, :keep_non_public) do + log_message <> ", keeping non public posts" + else + log_message + end - from(o in Object, - where: - fragment( - "?->'to' \\? ? OR ?->'cc' \\? ?", - o.data, - ^Pleroma.Constants.as_public(), - o.data, - ^Pleroma.Constants.as_public() - ), - where: o.inserted_at < ^time_deadline, - where: + log_message = + if Keyword.get(options, :keep_threads) do + log_message <> ", keeping threads intact" + else + log_message + end + + log_message = + if Keyword.get(options, :prune_orphaned_activities) do + log_message <> ", pruning orphaned activities" + else + log_message + end + + log_message = + if Keyword.get(options, :vacuum) do + log_message <> + ", doing a full vacuum (you shouldn't do this as a recurring maintanance task)" + else + log_message + end + + Logger.info(log_message) + + if Keyword.get(options, :keep_threads) do + # We want to delete objects from threads where + # 1. the newest post is still old + # 2. none of the activities is local + # 3. none of the activities is bookmarked + # 4. optionally none of the posts is non-public + deletable_context = + if Keyword.get(options, :keep_non_public) do + Pleroma.Activity + |> join(:left, [a], b in Pleroma.Bookmark, on: a.id == b.activity_id) + |> group_by([a], fragment("? ->> 'context'::text", a.data)) + |> having( + [a], + not fragment( + # Posts (checked on Create Activity) is non-public + "bool_or((not(?->'to' \\? ? OR ?->'cc' \\? ?)) and ? ->> 'type' = 'Create')", + a.data, + ^Pleroma.Constants.as_public(), + a.data, + ^Pleroma.Constants.as_public(), + a.data + ) + ) + else + Pleroma.Activity + |> join(:left, [a], b in Pleroma.Bookmark, on: a.id == b.activity_id) + |> group_by([a], fragment("? ->> 'context'::text", a.data)) + end + |> having([a], max(a.updated_at) < ^time_deadline) + |> having([a], not fragment("bool_or(?)", a.local)) + |> having([_, b], fragment("max(?::text) is null", b.id)) + |> select([a], fragment("? ->> 'context'::text", a.data)) + + Pleroma.Object + |> where([o], fragment("? ->> 'context'::text", o.data) in subquery(deletable_context)) + else + if Keyword.get(options, :keep_non_public) do + Pleroma.Object + |> where( + [o], + fragment( + "?->'to' \\? ? OR ?->'cc' \\? ?", + o.data, + ^Pleroma.Constants.as_public(), + o.data, + ^Pleroma.Constants.as_public() + ) + ) + else + Pleroma.Object + end + |> where([o], o.updated_at < ^time_deadline) + |> where( + [o], fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host()) - ) + ) + end |> Repo.delete_all(timeout: :infinity) - prune_hashtags_query = """ + if !Keyword.get(options, :keep_threads) do + # Without the --keep-threads option, it's possible that bookmarked + # objects have been deleted. We remove the corresponding bookmarks. + """ + delete from public.bookmarks + where id in ( + select b.id from public.bookmarks b + left join public.activities a on b.activity_id = a.id + left join public.objects o on a."data" ->> 'object' = o.data ->> 'id' + where o.id is null + ) + """ + |> Repo.query([], timeout: :infinity) + end + + if Keyword.get(options, :prune_orphaned_activities) do + # Prune activities who link to a single object + """ + delete from public.activities + where id in ( + select a.id from public.activities a + left join public.objects o on a.data ->> 'object' = o.data ->> 'id' + left join public.activities a2 on a.data ->> 'object' = a2.data ->> 'id' + left join public.users u on a.data ->> 'object' = u.ap_id + where not a.local + and jsonb_typeof(a."data" -> 'object') = 'string' + and o.id is null + and a2.id is null + and u.id is null + ) + """ + |> Repo.query([], timeout: :infinity) + + # Prune activities who link to an array of objects + """ + delete from public.activities + where id in ( + select a.id from public.activities a + join json_array_elements_text((a."data" -> 'object')::json) as j on jsonb_typeof(a."data" -> 'object') = 'array' + left join public.objects o on j.value = o.data ->> 'id' + left join public.activities a2 on j.value = a2.data ->> 'id' + left join public.users u on j.value = u.ap_id + group by a.id + having max(o.data ->> 'id') is null + and max(a2.data ->> 'id') is null + and max(u.ap_id) is null + ) + """ + |> Repo.query([], timeout: :infinity) + end + + """ DELETE FROM hashtags AS ht WHERE NOT EXISTS ( SELECT 1 FROM hashtags_objects hto WHERE ht.id = hto.hashtag_id) """ - - Repo.query(prune_hashtags_query) + |> Repo.query() if Keyword.get(options, :vacuum) do Maintenance.vacuum("full") -- cgit v1.2.3 From cd7e2138d11901fc7a0c8c2f22b7a5d57383a555 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 14 May 2024 14:13:37 +0400 Subject: Search: Basic Qdrant/Ollama search --- lib/mix/tasks/pleroma/search/indexer.ex | 60 ++++++++++++++++ lib/pleroma/search/qdrant_search.ex | 117 ++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 lib/mix/tasks/pleroma/search/indexer.ex create mode 100644 lib/pleroma/search/qdrant_search.ex (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/search/indexer.ex b/lib/mix/tasks/pleroma/search/indexer.ex new file mode 100644 index 000000000..ffa2f3c94 --- /dev/null +++ b/lib/mix/tasks/pleroma/search/indexer.ex @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.Search.Indexer do + import Mix.Pleroma + import Ecto.Query + + alias Pleroma.Workers.SearchIndexingWorker + + def run(["index" | options]) do + {options, [], []} = + OptionParser.parse( + options, + strict: [ + limit: :integer + ] + ) + + start_pleroma() + + limit = Keyword.get(options, :limit, 100_000) + + per_step = 1000 + chunks = max(div(limit, per_step), 1) + + 1..chunks + |> Enum.each(fn step -> + q = + from(a in Pleroma.Activity, + limit: ^per_step, + offset: ^per_step * (^step - 1), + select: [:id], + order_by: [desc: :id] + ) + + {:ok, ids} = + Pleroma.Repo.transaction(fn -> + Pleroma.Repo.stream(q, timeout: :infinity) + |> Enum.map(fn a -> + a.id + end) + end) + + IO.puts("Got #{length(ids)} activities, adding to indexer") + + ids + |> Enum.chunk_every(100) + |> Enum.each(fn chunk -> + IO.puts("Adding #{length(chunk)} activities to indexing queue") + + chunk + |> Enum.map(fn id -> + SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => id}) + end) + |> Oban.insert_all() + end) + end) + end +end diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex new file mode 100644 index 000000000..fcaa9e686 --- /dev/null +++ b/lib/pleroma/search/qdrant_search.ex @@ -0,0 +1,117 @@ +defmodule Pleroma.Search.QdrantSearch do + @behaviour Pleroma.Search.SearchBackend + import Ecto.Query + alias Pleroma.Activity + + alias __MODULE__.QdrantClient + alias __MODULE__.OllamaClient + + import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] + + def initialize_index() do + payload = Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) + QdrantClient.put("/collections/posts", payload) + end + + def drop_index() do + QdrantClient.delete("/collections/posts") + end + + def get_embedding(text) do + with {:ok, %{body: %{"embedding" => embedding}}} <- + OllamaClient.post("/api/embeddings", %{ + prompt: text, + model: Pleroma.Config.get([Pleroma.Search.QdrantSearch, :ollama_model]) + }) + |> IO.inspect() do + {:ok, embedding} + else + _ -> + {:error, "Failed to get embedding"} + end + end + + defp build_index_payload(activity, embedding) do + %{ + points: [ + %{ + id: activity.id |> FlakeId.from_string() |> Ecto.UUID.cast!(), + vector: embedding + } + ] + } + end + + defp build_search_payload(embedding) do + %{ + vector: embedding, + limit: 20 + } + end + + @impl true + def add_to_index(activity) do + # This will only index public or unlisted notes + maybe_search_data = object_to_search_data(activity.object) + IO.puts("TRYING TO INDEX\n\n") + + if activity.data["type"] == "Create" and maybe_search_data do + with {:ok, embedding} <- get_embedding(maybe_search_data.content), + {:ok, %{status: 200}} <- + QdrantClient.put( + "/collections/posts/points", + build_index_payload(activity, embedding) + ) do + :ok + else + e -> {:error, e} + end + else + :ok + end + end + + @impl true + def search(_user, query, _options) do + with {:ok, embedding} <- get_embedding(query), + {:ok, %{body: %{"result" => result}}} <- + QdrantClient.post("/collections/posts/points/search", build_search_payload(embedding)) do + ids = + Enum.map(result, fn %{"id" => id} -> + Ecto.UUID.dump!(id) + end) + + from(a in Activity, where: a.id in ^ids) + |> Activity.with_preloaded_object() + |> Activity.restrict_deactivated_users() + |> Ecto.Query.order_by([a], fragment("array_position(?, ?)", ^ids, a.id)) + |> Pleroma.Repo.all() + else + _ -> + [] + end + end + + @impl true + def remove_from_index(_object) do + :ok + end +end + +defmodule Pleroma.Search.QdrantSearch.OllamaClient do + use Tesla + + plug(Tesla.Middleware.BaseUrl, Pleroma.Config.get([Pleroma.Search.QdrantSearch, :ollama_url])) + plug(Tesla.Middleware.JSON) +end + +defmodule Pleroma.Search.QdrantSearch.QdrantClient do + use Tesla + + plug(Tesla.Middleware.BaseUrl, Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_url])) + plug(Tesla.Middleware.JSON) + + plug(Tesla.Middleware.Headers, [ + {"api-key", Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_api_key])} + ]) +end -- cgit v1.2.3 From bb08a766f4c1bd84c98e245c1871c46fcc7c7a8d Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 14 May 2024 14:26:41 +0400 Subject: QdrantSearch: Remove debugging stuff --- lib/pleroma/search/qdrant_search.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index fcaa9e686..726a30b3b 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -22,8 +22,7 @@ defmodule Pleroma.Search.QdrantSearch do OllamaClient.post("/api/embeddings", %{ prompt: text, model: Pleroma.Config.get([Pleroma.Search.QdrantSearch, :ollama_model]) - }) - |> IO.inspect() do + }) do {:ok, embedding} else _ -> @@ -53,7 +52,6 @@ defmodule Pleroma.Search.QdrantSearch do def add_to_index(activity) do # This will only index public or unlisted notes maybe_search_data = object_to_search_data(activity.object) - IO.puts("TRYING TO INDEX\n\n") if activity.data["type"] == "Create" and maybe_search_data do with {:ok, embedding} <- get_embedding(maybe_search_data.content), -- cgit v1.2.3 From 1490ff30af7001adc386b4fec54c62e1a524d7d6 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 14 May 2024 15:09:38 +0400 Subject: QdrantSearch: Add query prefix. --- lib/pleroma/search/qdrant_search.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 726a30b3b..31e7754ae 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -71,6 +71,8 @@ defmodule Pleroma.Search.QdrantSearch do @impl true def search(_user, query, _options) do + query = "Represent this sentence for searching relevant passages: #{query}" + with {:ok, embedding} <- get_embedding(query), {:ok, %{body: %{"result" => result}}} <- QdrantClient.post("/collections/posts/points/search", build_search_payload(embedding)) do -- cgit v1.2.3 From 1261c43a7af48ed6e6753461944659391c4c58cc Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 14 May 2024 17:19:36 +0400 Subject: SearchBackend: Add create_index --- lib/mix/tasks/pleroma/search/indexer.ex | 6 ++++++ lib/pleroma/search/qdrant_search.ex | 3 ++- lib/pleroma/search/search_backend.ex | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/search/indexer.ex b/lib/mix/tasks/pleroma/search/indexer.ex index ffa2f3c94..326646b69 100644 --- a/lib/mix/tasks/pleroma/search/indexer.ex +++ b/lib/mix/tasks/pleroma/search/indexer.ex @@ -8,6 +8,12 @@ defmodule Mix.Tasks.Pleroma.Search.Indexer do alias Pleroma.Workers.SearchIndexingWorker + def run(["create_index"]) do + Application.ensure_all_started(:pleroma) + + Pleroma.Config.get([Pleroma.Search, :module]).create_index() + end + def run(["index" | options]) do {options, [], []} = OptionParser.parse( diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 31e7754ae..315262cb3 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -8,7 +8,8 @@ defmodule Pleroma.Search.QdrantSearch do import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] - def initialize_index() do + @impl true + def create_index() do payload = Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) QdrantClient.put("/collections/posts", payload) end diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex index 68bc48cec..5be0169d0 100644 --- a/lib/pleroma/search/search_backend.ex +++ b/lib/pleroma/search/search_backend.ex @@ -21,4 +21,9 @@ defmodule Pleroma.Search.SearchBackend do from index. """ @callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()} + + @doc """ + Create the index + """ + @callback create_index() :: :ok | {:error, any()} end -- cgit v1.2.3 From a9be4907c0d7b34e5564584d2d040632c32f2aa3 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 16 May 2024 10:47:24 +0400 Subject: SearchBackend: Add drop_index --- lib/mix/tasks/pleroma/search/indexer.ex | 18 ++++++++++++++++-- lib/pleroma/search/database_search.ex | 6 ++++++ lib/pleroma/search/meilisearch.ex | 6 ++++++ lib/pleroma/search/qdrant_search.ex | 14 ++++++++++++-- lib/pleroma/search/search_backend.ex | 5 +++++ 5 files changed, 45 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/search/indexer.ex b/lib/mix/tasks/pleroma/search/indexer.ex index 326646b69..81a9fced6 100644 --- a/lib/mix/tasks/pleroma/search/indexer.ex +++ b/lib/mix/tasks/pleroma/search/indexer.ex @@ -9,9 +9,23 @@ defmodule Mix.Tasks.Pleroma.Search.Indexer do alias Pleroma.Workers.SearchIndexingWorker def run(["create_index"]) do - Application.ensure_all_started(:pleroma) + start_pleroma() + + with :ok <- Pleroma.Config.get([Pleroma.Search, :module]).create_index() do + IO.puts("Index created") + else + e -> IO.puts("Could not create index: #{inspect(e)}") + end + end + + def run(["drop_index"]) do + start_pleroma() - Pleroma.Config.get([Pleroma.Search, :module]).create_index() + with :ok <- Pleroma.Config.get([Pleroma.Search, :module]).drop_index() do + IO.puts("Index dropped") + else + e -> IO.puts("Could not drop index: #{inspect(e)}") + end end def run(["index" | options]) do diff --git a/lib/pleroma/search/database_search.ex b/lib/pleroma/search/database_search.ex index 31bfc7e33..24a1ff431 100644 --- a/lib/pleroma/search/database_search.ex +++ b/lib/pleroma/search/database_search.ex @@ -48,6 +48,12 @@ defmodule Pleroma.Search.DatabaseSearch do @impl true def remove_from_index(_object), do: :ok + @impl true + def create_index, do: :ok + + @impl true + def drop_index, do: :ok + def maybe_restrict_author(query, %User{} = author) do Activity.Queries.by_author(query, author) end diff --git a/lib/pleroma/search/meilisearch.ex b/lib/pleroma/search/meilisearch.ex index 2bff663e8..50f5984d6 100644 --- a/lib/pleroma/search/meilisearch.ex +++ b/lib/pleroma/search/meilisearch.ex @@ -10,6 +10,12 @@ defmodule Pleroma.Search.Meilisearch do @behaviour Pleroma.Search.SearchBackend + @impl true + def create_index, do: :ok + + @impl true + def drop_index, do: :ok + defp meili_headers do private_key = Config.get([Pleroma.Search.Meilisearch, :private_key]) diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 315262cb3..4bd35c17c 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -11,11 +11,21 @@ defmodule Pleroma.Search.QdrantSearch do @impl true def create_index() do payload = Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) - QdrantClient.put("/collections/posts", payload) + + with {:ok, %{status: 200}} <- QdrantClient.put("/collections/posts", payload) do + :ok + else + e -> {:error, e} + end end + @impl true def drop_index() do - QdrantClient.delete("/collections/posts") + with {:ok, %{status: 200}} <- QdrantClient.delete("/collections/posts") do + :ok + else + e -> {:error, e} + end end def get_embedding(text) do diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex index 5be0169d0..9735ab3f4 100644 --- a/lib/pleroma/search/search_backend.ex +++ b/lib/pleroma/search/search_backend.ex @@ -26,4 +26,9 @@ defmodule Pleroma.Search.SearchBackend do Create the index """ @callback create_index() :: :ok | {:error, any()} + + @doc """ + Drop the index + """ + @callback drop_index() :: :ok | {:error, any()} end -- cgit v1.2.3 From 933117785fb1b5b671c61d09671cf6418b105187 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sat, 18 May 2024 13:43:47 +0400 Subject: QdrantSearch: Add basic test --- lib/pleroma/search/qdrant_search.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 4bd35c17c..2d9315a2f 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -5,12 +5,13 @@ defmodule Pleroma.Search.QdrantSearch do alias __MODULE__.QdrantClient alias __MODULE__.OllamaClient + alias Pleroma.Config.Getting, as: Config import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] @impl true def create_index() do - payload = Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) + payload = Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) with {:ok, %{status: 200}} <- QdrantClient.put("/collections/posts", payload) do :ok @@ -32,7 +33,7 @@ defmodule Pleroma.Search.QdrantSearch do with {:ok, %{body: %{"embedding" => embedding}}} <- OllamaClient.post("/api/embeddings", %{ prompt: text, - model: Pleroma.Config.get([Pleroma.Search.QdrantSearch, :ollama_model]) + model: Config.get([Pleroma.Search.QdrantSearch, :ollama_model]) }) do {:ok, embedding} else @@ -111,15 +112,17 @@ end defmodule Pleroma.Search.QdrantSearch.OllamaClient do use Tesla + alias Pleroma.Config.Getting, as: Config - plug(Tesla.Middleware.BaseUrl, Pleroma.Config.get([Pleroma.Search.QdrantSearch, :ollama_url])) + plug(Tesla.Middleware.BaseUrl, Config.get([Pleroma.Search.QdrantSearch, :ollama_url])) plug(Tesla.Middleware.JSON) end defmodule Pleroma.Search.QdrantSearch.QdrantClient do use Tesla + alias Pleroma.Config.Getting, as: Config - plug(Tesla.Middleware.BaseUrl, Pleroma.Config.get([Pleroma.Search.QdrantSearch, :qdrant_url])) + plug(Tesla.Middleware.BaseUrl, Config.get([Pleroma.Search.QdrantSearch, :qdrant_url])) plug(Tesla.Middleware.JSON) plug(Tesla.Middleware.Headers, [ -- cgit v1.2.3 From e3933a067feae1f087616f675657d6ff99b2782b Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sat, 18 May 2024 14:04:32 +0400 Subject: QdrantSearch: Implement post deletion --- lib/pleroma/search/qdrant_search.ex | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 2d9315a2f..acfaaff52 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -81,6 +81,19 @@ defmodule Pleroma.Search.QdrantSearch do end end + @impl true + def remove_from_index(object) do + activity = Activity.get_by_object_ap_id_with_object(object.data["id"]) + id = activity.id |> FlakeId.from_string() |> Ecto.UUID.cast!() + + with {:ok, %{status: 200}} <- + QdrantClient.post("/collections/posts/points/delete", %{"points" => [id]}) do + :ok + else + e -> {:error, e} + end + end + @impl true def search(_user, query, _options) do query = "Represent this sentence for searching relevant passages: #{query}" @@ -103,11 +116,6 @@ defmodule Pleroma.Search.QdrantSearch do [] end end - - @impl true - def remove_from_index(_object) do - :ok - end end defmodule Pleroma.Search.QdrantSearch.OllamaClient do -- cgit v1.2.3 From 3345ddd2d4ef380929cc231118a5fb6486c0bd5c Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sat, 18 May 2024 15:02:22 +0400 Subject: Linting --- lib/pleroma/search/qdrant_search.ex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index acfaaff52..a6c6c6a0d 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -1,16 +1,17 @@ defmodule Pleroma.Search.QdrantSearch do @behaviour Pleroma.Search.SearchBackend import Ecto.Query + alias Pleroma.Activity + alias Pleroma.Config.Getting, as: Config - alias __MODULE__.QdrantClient alias __MODULE__.OllamaClient - alias Pleroma.Config.Getting, as: Config + alias __MODULE__.QdrantClient import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] @impl true - def create_index() do + def create_index do payload = Config.get([Pleroma.Search.QdrantSearch, :qdrant_index_configuration]) with {:ok, %{status: 200}} <- QdrantClient.put("/collections/posts", payload) do @@ -21,7 +22,7 @@ defmodule Pleroma.Search.QdrantSearch do end @impl true - def drop_index() do + def drop_index do with {:ok, %{status: 200}} <- QdrantClient.delete("/collections/posts") do :ok else -- cgit v1.2.3 From d07d49227fd3bf716fa22e402685f27e31a0f6d3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 16 May 2024 16:26:21 -0400 Subject: PleromaAPI: marking notifications as read no longer returns notifications --- lib/pleroma/notification.ex | 17 ++++------------- .../operations/pleroma_notification_operation.ex | 8 +------- .../controllers/notification_controller.ex | 20 ++++++++++++-------- 3 files changed, 17 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index a80279fa6..cb9bd92b8 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -280,15 +280,10 @@ defmodule Pleroma.Notification do select: n.id ) - {:ok, %{ids: {_, notification_ids}}} = - Multi.new() - |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()]) - |> Marker.multi_set_last_read_id(user, "notifications") - |> Repo.transaction() - - for_user_query(user) - |> where([n], n.id in ^notification_ids) - |> Repo.all() + Multi.new() + |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()]) + |> Marker.multi_set_last_read_id(user, "notifications") + |> Repo.transaction() end @spec read_one(User.t(), String.t()) :: @@ -299,10 +294,6 @@ defmodule Pleroma.Notification do |> Multi.update(:update, changeset(notification, %{seen: true})) |> Marker.multi_set_last_read_id(user, "notifications") |> Repo.transaction() - |> case do - {:ok, %{update: notification}} -> {:ok, notification} - {:error, :update, changeset, _} -> {:error, changeset} - end end end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex index a994345db..0e2865191 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.NotificationOperation alias Pleroma.Web.ApiSpec.Schemas.ApiError import Pleroma.Web.ApiSpec.Helpers @@ -35,12 +34,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do Operation.response( "A Notification or array of Notifications", "application/json", - %Schema{ - anyOf: [ - %Schema{type: :array, items: NotificationOperation.notification()}, - NotificationOperation.notification() - ] - } + %Schema{type: :string} ), 400 => Operation.response("Bad Request", "application/json", ApiError) } diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex index f860eaf7e..435ccfabe 100644 --- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex @@ -23,8 +23,9 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do } = conn, _ ) do - with {:ok, notification} <- Notification.read_one(user, notification_id) do - render(conn, "show.json", notification: notification, for: user) + with {:ok, _} <- Notification.read_one(user, notification_id) do + conn + |> json("ok") else {:error, message} -> conn @@ -38,11 +39,14 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do conn, _ ) do - notifications = - user - |> Notification.set_read_up_to(max_id) - |> Enum.take(80) - - render(conn, "index.json", notifications: notifications, for: user) + with {:ok, _} <- Notification.set_read_up_to(user, max_id) do + conn + |> json("ok") + else + {:error, message} -> + conn + |> put_status(:bad_request) + |> json(%{"error" => message}) + end end end -- cgit v1.2.3 From 72ec261a69a7dda7ab95667e425824ab7758b636 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 19 May 2024 12:17:46 +0400 Subject: B QdrantSearch: Switch to OpenAI api --- lib/pleroma/search/qdrant_search.ex | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index a6c6c6a0d..5ae04be78 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Search.QdrantSearch do alias Pleroma.Activity alias Pleroma.Config.Getting, as: Config - alias __MODULE__.OllamaClient + alias __MODULE__.OpenAIClient alias __MODULE__.QdrantClient import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] @@ -31,10 +31,10 @@ defmodule Pleroma.Search.QdrantSearch do end def get_embedding(text) do - with {:ok, %{body: %{"embedding" => embedding}}} <- - OllamaClient.post("/api/embeddings", %{ - prompt: text, - model: Config.get([Pleroma.Search.QdrantSearch, :ollama_model]) + with {:ok, %{body: %{"data" => [%{"embedding" => embedding}]}}} <- + OpenAIClient.post("/v1/embeddings", %{ + input: text, + model: Config.get([Pleroma.Search.QdrantSearch, :openai_model]) }) do {:ok, embedding} else @@ -119,12 +119,17 @@ defmodule Pleroma.Search.QdrantSearch do end end -defmodule Pleroma.Search.QdrantSearch.OllamaClient do +defmodule Pleroma.Search.QdrantSearch.OpenAIClient do use Tesla alias Pleroma.Config.Getting, as: Config - plug(Tesla.Middleware.BaseUrl, Config.get([Pleroma.Search.QdrantSearch, :ollama_url])) + plug(Tesla.Middleware.BaseUrl, Config.get([Pleroma.Search.QdrantSearch, :openai_url])) plug(Tesla.Middleware.JSON) + + plug(Tesla.Middleware.Headers, [ + {"Authorization", + "Bearer #{Pleroma.Config.get([Pleroma.Search.QdrantSearch, :openai_api_key])}"} + ]) end defmodule Pleroma.Search.QdrantSearch.QdrantClient do -- cgit v1.2.3 From 1b4f1db9b2990f725a06f0dff41980c51853c5e9 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 19 May 2024 14:41:05 +0400 Subject: QdrantSearch: Support pagination. --- lib/pleroma/search/qdrant_search.ex | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 5ae04be78..283c43075 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -54,10 +54,11 @@ defmodule Pleroma.Search.QdrantSearch do } end - defp build_search_payload(embedding) do + defp build_search_payload(embedding, options) do %{ vector: embedding, - limit: 20 + limit: options[:limit] || 20, + offset: options[:offset] || 0 } end @@ -96,12 +97,15 @@ defmodule Pleroma.Search.QdrantSearch do end @impl true - def search(_user, query, _options) do + def search(_user, query, options) do query = "Represent this sentence for searching relevant passages: #{query}" with {:ok, embedding} <- get_embedding(query), {:ok, %{body: %{"result" => result}}} <- - QdrantClient.post("/collections/posts/points/search", build_search_payload(embedding)) do + QdrantClient.post( + "/collections/posts/points/search", + build_search_payload(embedding, options) + ) do ids = Enum.map(result, fn %{"id" => id} -> Ecto.UUID.dump!(id) -- cgit v1.2.3 From 36fa0debfe66d3b706eeaa09227edd8b82c70aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 20 May 2024 23:25:50 +0200 Subject: Fix `get_notified_from` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/notification.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 55c47e966..942aa7198 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -526,7 +526,7 @@ defmodule Pleroma.Notification do Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end) end - def get_notified_from_activity(_, _local_only), do: {[], []} + def get_notified_from_activity(_, _local_only), do: [] def get_notified_subscribers_from_activity(activity, local_only \\ true) @@ -544,7 +544,7 @@ defmodule Pleroma.Notification do Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end) end - def get_notified_subscribers_from_activity(_, _), do: {[], []} + def get_notified_subscribers_from_activity(_, _), do: [] # For some activities, only notify the author of the object def get_potential_receiver_ap_ids(%{data: %{"type" => type, "object" => object_id}}) -- cgit v1.2.3 From b15f8b06425edbfc3a7cef2a55c609b12ee14377 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 23 Aug 2023 13:10:19 -0500 Subject: Prevent webfinger spoofing --- lib/pleroma/web/web_finger.ex | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 26fb8af84..a84a4351b 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -216,10 +216,26 @@ defmodule Pleroma.Web.WebFinger do _ -> {:error, {:content_type, nil}} end + |> case do + {:ok, data} -> validate_webfinger(address, data) + error -> error + end else error -> Logger.debug("Couldn't finger #{account}: #{inspect(error)}") error end end + + defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do + with %URI{host: request_host} <- URI.parse(url), + [_name, acct_host] <- String.split(acct, "@"), + {_, true} <- {:hosts_match, acct_host == request_host} do + {:ok, data} + else + _ -> {:error, {:webfinger_invalid, url, data}} + end + end + + defp validate_webfinger(url, data), do: {:error, {:webfinger_invalid, url, data}} end -- cgit v1.2.3 From 84bb854056e406d5235dd442c28127891a8a8a86 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 15:12:29 +0400 Subject: Webfinger: Allow managing account for subdomain --- lib/pleroma/web/web_finger.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index a84a4351b..e149d9247 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -230,7 +230,7 @@ defmodule Pleroma.Web.WebFinger do defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do with %URI{host: request_host} <- URI.parse(url), [_name, acct_host] <- String.split(acct, "@"), - {_, true} <- {:hosts_match, acct_host == request_host} do + {_, true} <- {:hosts_match_or_subdomain, String.ends_with?(request_host, acct_host)} do {:ok, data} else _ -> {:error, {:webfinger_invalid, url, data}} -- cgit v1.2.3 From 1f2f7e044d1be1e56789ce01ce4e54dd86a74f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 22 May 2024 15:52:10 +0200 Subject: Revert "Webfinger: Allow managing account for subdomain" This reverts commit 84bb854056e406d5235dd442c28127891a8a8a86. --- lib/pleroma/web/web_finger.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index e149d9247..a84a4351b 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -230,7 +230,7 @@ defmodule Pleroma.Web.WebFinger do defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do with %URI{host: request_host} <- URI.parse(url), [_name, acct_host] <- String.split(acct, "@"), - {_, true} <- {:hosts_match_or_subdomain, String.ends_with?(request_host, acct_host)} do + {_, true} <- {:hosts_match, acct_host == request_host} do {:ok, data} else _ -> {:error, {:webfinger_invalid, url, data}} -- cgit v1.2.3 From d0b18e338bfed05c6b2c4a8f5c63d865d9eb669c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 24 Aug 2023 00:37:39 +0200 Subject: Fix validate_webfinger when running a different domain for Webfinger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/application.ex | 3 ++- lib/pleroma/web/web_finger.ex | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 75154f94c..649bb11c8 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -162,7 +162,8 @@ defmodule Pleroma.Application do expiration: chat_message_id_idempotency_key_expiration(), limit: 500_000 ), - build_cachex("rel_me", limit: 2500) + build_cachex("rel_me", limit: 2500), + build_cachex("host_meta", default_ttl: :timer.minutes(120), limit: 5000) ] end diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index a84a4351b..e653b3338 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -155,7 +155,16 @@ defmodule Pleroma.Web.WebFinger do end end + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) def find_lrdd_template(domain) do + @cachex.fetch!(:host_meta_cache, domain, fn _ -> + {:commit, fetch_lrdd_template(domain)} + end) + rescue + e -> {:error, "Cachex error: #{inspect(e)}"} + end + + defp fetch_lrdd_template(domain) do # WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1 meta_url = "https://#{domain}/.well-known/host-meta" @@ -168,7 +177,7 @@ defmodule Pleroma.Web.WebFinger do end end - defp get_address_from_domain(domain, encoded_account) when is_binary(domain) do + defp get_address_from_domain(domain, "acct:" <> _ = encoded_account) when is_binary(domain) do case find_lrdd_template(domain) do {:ok, template} -> String.replace(template, "{uri}", encoded_account) @@ -178,6 +187,11 @@ defmodule Pleroma.Web.WebFinger do end end + defp get_address_from_domain(domain, account) when is_binary(domain) do + encoded_account = URI.encode("acct:#{account}") + get_address_from_domain(domain, encoded_account) + end + defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain} @spec finger(String.t()) :: {:ok, map()} | {:error, any()} @@ -192,9 +206,7 @@ defmodule Pleroma.Web.WebFinger do URI.parse(account).host end - encoded_account = URI.encode("acct:#{account}") - - with address when is_binary(address) <- get_address_from_domain(domain, encoded_account), + with address when is_binary(address) <- get_address_from_domain(domain, account), {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- HTTP.get( address, @@ -227,13 +239,15 @@ defmodule Pleroma.Web.WebFinger do end end - defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do - with %URI{host: request_host} <- URI.parse(url), - [_name, acct_host] <- String.split(acct, "@"), + defp validate_webfinger(request_url, %{"subject" => "acct:" <> acct = subject} = data) do + with [_name, acct_host] <- String.split(acct, "@"), + {_, url} <- {:address, get_address_from_domain(acct_host, subject)}, + %URI{host: request_host} <- URI.parse(request_url), + %URI{host: acct_host} <- URI.parse(url), {_, true} <- {:hosts_match, acct_host == request_host} do {:ok, data} else - _ -> {:error, {:webfinger_invalid, url, data}} + _ -> {:error, {:webfinger_invalid, request_url, data}} end end -- cgit v1.2.3 From 94e4f215896dc7976a54fd146daf3e286602925a Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 23 May 2024 14:38:30 +0400 Subject: QdrantSearch: Deal with actor restrictions --- lib/pleroma/search/qdrant_search.ex | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 283c43075..9cb34ef71 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -43,23 +43,41 @@ defmodule Pleroma.Search.QdrantSearch do end end + defp actor_from_activity(%{data: %{"actor" => actor}}) do + actor + end + + defp actor_from_activity(_), do: nil + defp build_index_payload(activity, embedding) do + actor = actor_from_activity(activity) + published_at = activity.data["published"] + %{ points: [ %{ id: activity.id |> FlakeId.from_string() |> Ecto.UUID.cast!(), - vector: embedding + vector: embedding, + payload: %{actor: actor, published_at: published_at} } ] } end defp build_search_payload(embedding, options) do - %{ + base = %{ vector: embedding, limit: options[:limit] || 20, offset: options[:offset] || 0 } + + if options[:actor] do + Map.put(base, :filter, %{ + must: [%{key: "actor", match: %{value: options[:actor].ap_id}}] + }) + else + base + end end @impl true -- cgit v1.2.3 From a566ad56e1434715d00067b1e49be66b6787f5ba Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 23 May 2024 18:55:16 +0400 Subject: QdrantSearch: Fix actor / author restriction --- lib/pleroma/search/qdrant_search.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 9cb34ef71..19e8cd4bf 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -71,9 +71,9 @@ defmodule Pleroma.Search.QdrantSearch do offset: options[:offset] || 0 } - if options[:actor] do + if author = options[:author] do Map.put(base, :filter, %{ - must: [%{key: "actor", match: %{value: options[:actor].ap_id}}] + must: [%{key: "actor", match: %{value: author.ap_id}}] }) else base -- cgit v1.2.3 From 61a3b793165e92d6f23a2e59fb9b95e06737cf25 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 25 May 2024 14:20:47 -0400 Subject: Search backend healthcheck process --- lib/pleroma/application.ex | 3 +- lib/pleroma/search.ex | 5 +++ lib/pleroma/search/database_search.ex | 3 ++ lib/pleroma/search/healthcheck.ex | 85 +++++++++++++++++++++++++++++++++++ lib/pleroma/search/meilisearch.ex | 11 +++++ lib/pleroma/search/search_backend.ex | 8 ++++ 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/search/healthcheck.ex (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 649bb11c8..d266d1836 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -109,7 +109,8 @@ defmodule Pleroma.Application do streamer_registry() ++ background_migrators() ++ shout_child(shout_enabled?()) ++ - [Pleroma.Gopher.Server] + [Pleroma.Gopher.Server] ++ + [Pleroma.Search.Healthcheck] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options diff --git a/lib/pleroma/search.ex b/lib/pleroma/search.ex index 3b266e59b..e8dbcca1f 100644 --- a/lib/pleroma/search.ex +++ b/lib/pleroma/search.ex @@ -14,4 +14,9 @@ defmodule Pleroma.Search do search_module.search(options[:for_user], query, options) end + + def healthcheck_endpoints do + search_module = Pleroma.Config.get([Pleroma.Search, :module], Pleroma.Activity) + search_module.healthcheck_endpoints + end end diff --git a/lib/pleroma/search/database_search.ex b/lib/pleroma/search/database_search.ex index 31bfc7e33..11e99e7f1 100644 --- a/lib/pleroma/search/database_search.ex +++ b/lib/pleroma/search/database_search.ex @@ -48,6 +48,9 @@ defmodule Pleroma.Search.DatabaseSearch do @impl true def remove_from_index(_object), do: :ok + @impl true + def healthcheck_endpoints, do: nil + def maybe_restrict_author(query, %User{} = author) do Activity.Queries.by_author(query, author) end diff --git a/lib/pleroma/search/healthcheck.ex b/lib/pleroma/search/healthcheck.ex new file mode 100644 index 000000000..495aee930 --- /dev/null +++ b/lib/pleroma/search/healthcheck.ex @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Search.Healthcheck do + @doc """ + Monitors health of search backend to control processing of events based on health and availability. + """ + use GenServer + require Logger + + @tick :timer.seconds(60) + @queue :search_indexing + + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + @impl true + def init(_) do + state = %{healthy: false} + {:ok, state, {:continue, :start}} + end + + @impl true + def handle_continue(:start, state) do + tick() + {:noreply, state} + end + + @impl true + def handle_info(:check, state) do + urls = Pleroma.Search.healthcheck_endpoints() + + new_state = + if healthy?(urls) do + Oban.resume_queue(queue: @queue) + Map.put(state, :healthy, true) + else + Oban.pause_queue(queue: @queue) + Map.put(state, :healthy, false) + end + + maybe_log_state_change(state, new_state) + + tick() + {:noreply, new_state} + end + + @impl true + def handle_call(:check, _from, state) do + status = Map.get(state, :healthy) + + {:reply, status, state, :hibernate} + end + + defp healthy?([]), do: true + + defp healthy?(urls) when is_list(urls) do + Enum.all?( + urls, + fn url -> + case Pleroma.HTTP.get(url) do + {:ok, %{status: 200}} -> true + _ -> false + end + end + ) + end + + defp healthy?(_), do: true + + defp tick do + Process.send_after(self(), :check, @tick) + end + + defp maybe_log_state_change(%{healthy: true}, %{healthy: false}) do + Logger.error("Pausing Oban queue #{@queue} due to search backend healthcheck failure") + end + + defp maybe_log_state_change(%{healthy: false}, %{healthy: true}) do + Logger.info("Resuming Oban queue #{@queue} due to search backend healthcheck pass") + end + + defp maybe_log_state_change(_, _), do: :ok +end diff --git a/lib/pleroma/search/meilisearch.ex b/lib/pleroma/search/meilisearch.ex index 2bff663e8..08c2f3d86 100644 --- a/lib/pleroma/search/meilisearch.ex +++ b/lib/pleroma/search/meilisearch.ex @@ -178,4 +178,15 @@ defmodule Pleroma.Search.Meilisearch do def remove_from_index(object) do meili_delete("/indexes/objects/documents/#{object.id}") end + + @impl true + def healthcheck_endpoints do + endpoint = + Config.get([Pleroma.Search.Meilisearch, :url]) + |> URI.parse() + |> Map.put(:path, "/health") + |> URI.to_string() + + [endpoint] + end end diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex index 68bc48cec..13c887bc2 100644 --- a/lib/pleroma/search/search_backend.ex +++ b/lib/pleroma/search/search_backend.ex @@ -21,4 +21,12 @@ defmodule Pleroma.Search.SearchBackend do from index. """ @callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()} + + @doc """ + Healthcheck endpoints of search backend infrastructure to monitor for controlling + processing of jobs in the Oban queue. + + It is expected a 200 response is healthy and other responses are unhealthy. + """ + @callback healthcheck_endpoints :: list() | nil end -- cgit v1.2.3 From 3474b42ce396150b21f26ed35bea46ad61f57d5f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 25 May 2024 16:55:29 -0400 Subject: Drop TTL to 5 seconds --- lib/pleroma/search/healthcheck.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/search/healthcheck.ex b/lib/pleroma/search/healthcheck.ex index 495aee930..9a2d9fdd6 100644 --- a/lib/pleroma/search/healthcheck.ex +++ b/lib/pleroma/search/healthcheck.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Search.Healthcheck do use GenServer require Logger - @tick :timer.seconds(60) + @tick :timer.seconds(5) @queue :search_indexing def start_link(_) do -- cgit v1.2.3 From 807782b7f96ee0e053ad59b464766d750f8a8800 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 25 May 2024 16:27:59 -0400 Subject: Fix rich media parsing some Amazon URLs --- lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex index 948c727e1..1172a120a 100644 --- a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex +++ b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do %URI{host: host, query: query} = URI.parse(image) is_binary(host) and String.contains?(host, "amazonaws.com") and - String.contains?(query, "X-Amz-Expires") + is_binary(query) and String.contains?(query, "X-Amz-Expires") end defp aws_signed_url?(_), do: nil -- cgit v1.2.3 From f2b0d5f1d02e243a7a1a6f339b59e5abcb8e1bd8 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 26 May 2024 14:11:41 -0400 Subject: Make it easier to read the state for debugging purposes and expose functions for testing --- lib/pleroma/search/healthcheck.ex | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/healthcheck.ex b/lib/pleroma/search/healthcheck.ex index 9a2d9fdd6..170f29344 100644 --- a/lib/pleroma/search/healthcheck.ex +++ b/lib/pleroma/search/healthcheck.ex @@ -32,7 +32,7 @@ defmodule Pleroma.Search.Healthcheck do urls = Pleroma.Search.healthcheck_endpoints() new_state = - if healthy?(urls) do + if check(urls) do Oban.resume_queue(queue: @queue) Map.put(state, :healthy, true) else @@ -47,15 +47,15 @@ defmodule Pleroma.Search.Healthcheck do end @impl true - def handle_call(:check, _from, state) do - status = Map.get(state, :healthy) - - {:reply, status, state, :hibernate} + def handle_call(:state, _from, state) do + {:reply, state, state, :hibernate} end - defp healthy?([]), do: true + def state, do: GenServer.call(__MODULE__, :state) + + def check([]), do: true - defp healthy?(urls) when is_list(urls) do + def check(urls) when is_list(urls) do Enum.all?( urls, fn url -> @@ -67,7 +67,7 @@ defmodule Pleroma.Search.Healthcheck do ) end - defp healthy?(_), do: true + def check(_), do: true defp tick do Process.send_after(self(), :check, @tick) -- cgit v1.2.3 From d4769b076a95ce2281dba5673c410eb098445bba Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 26 May 2024 15:13:59 -0400 Subject: Return a 422 when trying to reply to a deleted status --- lib/pleroma/web/common_api/activity_draft.ex | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index bc46a8a36..8aa1e258d 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -129,8 +129,22 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft - defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do - %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)} + defp in_reply_to(%{params: %{in_reply_to_status_id: :deleted}} = draft) do + add_error(draft, dgettext("errors", "Cannot reply to a deleted status")) + end + + defp in_reply_to(%{params: %{in_reply_to_status_id: id} = params} = draft) when is_binary(id) do + activity = Activity.get_by_id(id) + + params = + if is_nil(activity) do + # Deleted activities are returned as nil + Map.put(params, :in_reply_to_status_id, :deleted) + else + Map.put(params, :in_reply_to_status_id, activity) + end + + in_reply_to(%{draft | params: params}) end defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do -- cgit v1.2.3 From d9b82255b9cf49176f8ef1d5a87abf7d80769a47 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 26 May 2024 15:23:12 -0400 Subject: Add an HTTP timeout for the healthcheck --- lib/pleroma/search/healthcheck.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/healthcheck.ex b/lib/pleroma/search/healthcheck.ex index 170f29344..e562c8478 100644 --- a/lib/pleroma/search/healthcheck.ex +++ b/lib/pleroma/search/healthcheck.ex @@ -8,8 +8,9 @@ defmodule Pleroma.Search.Healthcheck do use GenServer require Logger - @tick :timer.seconds(5) @queue :search_indexing + @tick :timer.seconds(5) + @timeout :timer.seconds(2) def start_link(_) do GenServer.start_link(__MODULE__, [], name: __MODULE__) @@ -59,7 +60,7 @@ defmodule Pleroma.Search.Healthcheck do Enum.all?( urls, fn url -> - case Pleroma.HTTP.get(url) do + case Pleroma.HTTP.get(url, [], recv_timeout: @timeout) do {:ok, %{status: 200}} -> true _ -> false end -- cgit v1.2.3 From d35b69d2686e62cc5076bd7a33449f98f8a11a85 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 13:18:02 +0400 Subject: Pleroma.Search: Remove wrong (but irrelevant) results --- lib/pleroma/search.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search.ex b/lib/pleroma/search.ex index e8dbcca1f..fd0218cb8 100644 --- a/lib/pleroma/search.ex +++ b/lib/pleroma/search.ex @@ -10,13 +10,12 @@ defmodule Pleroma.Search do end def search(query, options) do - search_module = Pleroma.Config.get([Pleroma.Search, :module], Pleroma.Activity) - + search_module = Pleroma.Config.get([Pleroma.Search, :module]) search_module.search(options[:for_user], query, options) end def healthcheck_endpoints do - search_module = Pleroma.Config.get([Pleroma.Search, :module], Pleroma.Activity) + search_module = Pleroma.Config.get([Pleroma.Search, :module]) search_module.healthcheck_endpoints end end -- cgit v1.2.3 From 8b76f56050a609bf562053cb7201a9204901490e Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 13:57:42 +0400 Subject: QdrantSearch: Add healthcheck for qdrant --- lib/pleroma/search/qdrant_search.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 19e8cd4bf..3c3ffce16 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -139,6 +139,17 @@ defmodule Pleroma.Search.QdrantSearch do [] end end + + @impl true + def healthcheck_endpoints do + qdrant_health = + Config.get([Pleroma.Search.QdrantSearch, :qdrant_url]) + |> URI.parse() + |> Map.put(:path, "/healthz") + |> URI.to_string() + + [qdrant_health] + end end defmodule Pleroma.Search.QdrantSearch.OpenAIClient do -- cgit v1.2.3 From f4c04e6b2dce6d75d148ca520aaef27005ecaa82 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 14:21:55 +0400 Subject: QdrantSearch: Add health checks. --- lib/pleroma/search/qdrant_search.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 3c3ffce16..429ae05b8 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -148,7 +148,9 @@ defmodule Pleroma.Search.QdrantSearch do |> Map.put(:path, "/healthz") |> URI.to_string() - [qdrant_health] + openai_health = Config.get([Pleroma.Search.QdrantSearch, :openai_healthcheck_url]) + + [qdrant_health, openai_health] |> Enum.filter(& &1) end end -- cgit v1.2.3 From ddf103eca04c9571ba8310915556cc51cd4a9af8 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 14:35:08 +0400 Subject: QdrantSearch: Fetch a post in search if possible. --- lib/pleroma/search/qdrant_search.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/qdrant_search.ex b/lib/pleroma/search/qdrant_search.ex index 429ae05b8..b659bb682 100644 --- a/lib/pleroma/search/qdrant_search.ex +++ b/lib/pleroma/search/qdrant_search.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Search.QdrantSearch do alias __MODULE__.QdrantClient import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1] + import Pleroma.Search.DatabaseSearch, only: [maybe_fetch: 3] @impl true def create_index do @@ -115,8 +116,8 @@ defmodule Pleroma.Search.QdrantSearch do end @impl true - def search(_user, query, options) do - query = "Represent this sentence for searching relevant passages: #{query}" + def search(user, original_query, options) do + query = "Represent this sentence for searching relevant passages: #{original_query}" with {:ok, embedding} <- get_embedding(query), {:ok, %{body: %{"result" => result}}} <- @@ -134,6 +135,7 @@ defmodule Pleroma.Search.QdrantSearch do |> Activity.restrict_deactivated_users() |> Ecto.Query.order_by([a], fragment("array_position(?, ?)", ^ids, a.id)) |> Pleroma.Repo.all() + |> maybe_fetch(user, original_query) else _ -> [] -- cgit v1.2.3 From 3055c1598b43ee9460b88880e2752c68e9cf6edb Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 17:22:18 +0400 Subject: IPFSTest: Fix configuration mocking --- lib/pleroma/upload.ex | 2 +- lib/pleroma/uploaders/ipfs.ex | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 2c6b23c39..35c7c02a5 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -282,7 +282,7 @@ defmodule Pleroma.Upload do end Pleroma.Uploaders.IPFS -> - Config.get([Pleroma.Uploaders.IPFS, :get_gateway_url]) + @config_impl.get([Pleroma.Uploaders.IPFS, :get_gateway_url]) _ -> public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/" diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index 32e06c5cf..d171e4652 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -6,11 +6,12 @@ defmodule Pleroma.Uploaders.IPFS do @behaviour Pleroma.Uploaders.Uploader require Logger - alias Pleroma.Config alias Tesla.Multipart + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + defp get_final_url(method) do - config = Config.get([__MODULE__]) + config = @config_impl.get([__MODULE__]) post_base_url = Keyword.get(config, :post_gateway_url) Path.join([post_base_url, method]) @@ -69,7 +70,7 @@ defmodule Pleroma.Uploaders.IPFS do @impl true def delete_file(file) do case Pleroma.HTTP.post(delete_file_endpoint(), "", [], params: [arg: file]) do - {:ok, %{status_code: 204}} -> :ok + {:ok, %{status: 204}} -> :ok error -> {:error, inspect(error)} end end -- cgit v1.2.3 From a50c657427a2dfe9d48c25529a179fe634d30e48 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 11:17:02 -0400 Subject: Add a dedicated connection pool for Rich Media Sharing this pool with regular Media is problematic as Rich Media will connect to many different domains and thrash the pool, but regular Media will have predictable connections to the webservers hosting media for the fediverse servers you peer with. --- lib/pleroma/web/rich_media/helpers.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 119994458..ea41bd285 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -58,7 +58,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do defp http_options do [ - pool: :media, + pool: :rich_media, max_body: Config.get([:rich_media, :max_body], 5_000_000) ] end -- cgit v1.2.3 From d272eb62cd4da45e5ef82fcc0126d2cf799d292a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 11:22:45 -0400 Subject: Trust the connection pools to enforce the concurrency limitations --- lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index c95d35bb9..f10dc3ce5 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -30,9 +30,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do if Pleroma.Config.get(:env) == :test do fetch(prefetch_url) else - ConcurrentLimiter.limit(__MODULE__, fn -> - Task.start(fn -> fetch(prefetch_url) end) - end) + Task.start(fn -> fetch(prefetch_url) end) end end end -- cgit v1.2.3 From 37d79b76bb770cb294c0a54777b435dc49c042ab Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 11:24:54 -0400 Subject: Use the configured http client options for mediaproxy --- lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex | 10 ++++------ lib/pleroma/web/media_proxy/media_proxy_controller.ex | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index f10dc3ce5..e5eb6896a 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -11,11 +11,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do require Logger - @adapter_options [ - pool: :media, - recv_timeout: 10_000 - ] - @impl true def history_awareness, do: :auto @@ -35,7 +30,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do end end - defp fetch(url), do: HTTP.get(url, [], @adapter_options) + defp fetch(url) do + http_client_opts = Pleroma.Config.get([:media_proxy, :proxy_opts, :http], pool: :media) + HTTP.get(url, [], http_client_opts) + end defp preload(%{"object" => %{"attachment" => attachments}} = _message) do Enum.each(attachments, fn diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index c11484ecb..0b446e0a6 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -54,9 +54,10 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do defp handle_preview(conn, url) do media_proxy_url = MediaProxy.url(url) + http_client_opts = Pleroma.Config.get([:media_proxy, :proxy_opts, :http], pool: :media) with {:ok, %{status: status} = head_response} when status in 200..299 <- - Pleroma.HTTP.request(:head, media_proxy_url, "", [], pool: :media) do + Pleroma.HTTP.request(:head, media_proxy_url, "", [], http_client_opts) do content_type = Tesla.get_header(head_response, "content-type") content_length = Tesla.get_header(head_response, "content-length") content_length = content_length && String.to_integer(content_length) -- cgit v1.2.3 From e4f1325f78e9be9fb200358d73794f15794c39bd Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 19:44:41 +0400 Subject: InetHelper: Don't use deprecated function. --- lib/pleroma/helpers/inet_helper.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex index 3500fc679..00e18649e 100644 --- a/lib/pleroma/helpers/inet_helper.ex +++ b/lib/pleroma/helpers/inet_helper.ex @@ -25,6 +25,6 @@ defmodule Pleroma.Helpers.InetHelper do InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128" end - InetCidr.parse(proxy, true) + InetCidr.parse_cidr!(proxy, true) end end -- cgit v1.2.3 From 0bddca361d12f347ca9907c5ddb5d1464a17b32a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 24 Jan 2021 14:56:45 -0600 Subject: DNSRBL in an MRF --- lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex | 142 ++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex b/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex new file mode 100644 index 000000000..9543cc545 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex @@ -0,0 +1,142 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do + @moduledoc """ + Dynamic activity filtering based on an RBL database + + This MRF makes queries to a custom DNS server which will + respond with values indicating the classification of the domain + the activity originated from. This method has been widely used + in the email anti-spam industry for very fast reputation checks. + + e.g., if the DNS response is 127.0.0.1 or empty, the domain is OK + Other values such as 127.0.0.2 may be used for specific classifications. + + Information for why the host is blocked can be stored in a corresponding TXT record. + + This method is fail-open so if the queries fail the activites are accepted. + + An example of software meant for this purpsoe is rbldnsd which can be found + at http://www.corpit.ru/mjt/rbldnsd.html or mirrored at + https://git.pleroma.social/feld/rbldnsd + + It is highly recommended that you run your own copy of rbldnsd and use an + external mechanism to sync/share the contents of the zone file. This is + important to keep the latency on the queries as low as possible and prevent + your DNS server from being attacked so it fails and content is permitted. + """ + + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + alias Pleroma.Config + + require Logger + + @query_retries 1 + @query_timeout 500 + + @impl true + def filter(%{"actor" => actor} = object) do + actor_info = URI.parse(actor) + + with {:ok, object} <- check_rbl(actor_info, object) do + {:ok, object} + else + _ -> {:reject, "[DNSRBLPolicy]"} + end + end + + @impl true + def filter(object), do: {:ok, object} + + @impl true + def describe do + mrf_dnsrbl = + Config.get(:mrf_dnsrbl) + |> Enum.into(%{}) + + {:ok, %{mrf_dnsrbl: mrf_dnsrbl}} + end + + @impl true + def config_description do + %{ + key: :mrf_dnsrbl, + related_policy: "Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy", + label: "MRF DNSRBL", + description: "DNS RealTime Blackhole Policy", + children: [ + %{ + key: :nameserver, + type: {:string}, + description: "DNSRBL Nameserver to Query (IP or hostame)", + suggestions: ["127.0.0.1"] + }, + %{ + key: :port, + type: {:string}, + description: "Nameserver port", + suggestions: ["53"] + }, + %{ + key: :zone, + type: {:string}, + description: "Root zone for querying", + suggestions: ["bl.pleroma.com"] + } + ] + } + end + + defp check_rbl(%{host: actor_host}, object) do + with false <- match?(^actor_host, Pleroma.Web.Endpoint.host()), + zone when not is_nil(zone) <- Keyword.get(Config.get([:mrf_dnsrbl]), :zone) do + query = + Enum.join([actor_host, zone], ".") + |> String.to_charlist() + + rbl_response = rblquery(query) + + if Enum.empty?(rbl_response) do + {:ok, object} + else + Task.start(fn -> + reason = rblquery(query, :txt) || "undefined" + + Logger.warning( + "DNSRBL Rejected activity from #{actor_host} for reason: #{inspect(reason)}" + ) + end) + + :error + end + else + _ -> {:ok, object} + end + end + + defp get_rblhost_ip(rblhost) do + case rblhost |> String.to_charlist() |> :inet_parse.address() do + {:ok, _} -> rblhost |> String.to_charlist() |> :inet_parse.address() + _ -> {:ok, rblhost |> String.to_charlist() |> :inet_res.lookup(:in, :a) |> Enum.random()} + end + end + + defp rblquery(query, type \\ :a) do + config = Config.get([:mrf_dnsrbl]) + + case get_rblhost_ip(config[:nameserver]) do + {:ok, rblnsip} -> + :inet_res.lookup(query, :in, type, + nameservers: [{rblnsip, config[:port]}], + timeout: @query_timeout, + retry: @query_retries + ) + + _ -> + [] + end + end +end -- cgit v1.2.3 From 5e963736cee55aa8f4bb9d9fba451ff3864ddaa8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 21 May 2023 15:26:02 -0500 Subject: Add AntiMentionSpamPolicy --- .../activity_pub/mrf/anti_mention_spam_policy.ex | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex new file mode 100644 index 000000000..ad97a1552 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex @@ -0,0 +1,87 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do + alias Pleroma.User + require Pleroma.Constants + + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + defp user_has_followers?(%User{} = u), do: u.follower_count > 0 + defp user_has_posted?(%User{} = u), do: u.note_count > 0 + + defp user_has_age?(%User{} = u) do + now = NaiveDateTime.utc_now() + diff = u.inserted_at |> NaiveDateTime.diff(now, :second) + diff > :timer.seconds(30) + end + + defp good_reputation?(%User{} = u) do + user_has_age?(u) and user_has_followers?(u) and user_has_posted?(u) + end + + # copied from HellthreadPolicy + defp get_recipient_count(message) do + recipients = (message["to"] || []) ++ (message["cc"] || []) + + follower_collection = + User.get_cached_by_ap_id(message["actor"] || message["attributedTo"]).follower_address + + if Enum.member?(recipients, Pleroma.Constants.as_public()) do + recipients = + recipients + |> List.delete(Pleroma.Constants.as_public()) + |> List.delete(follower_collection) + + {:public, length(recipients)} + else + recipients = + recipients + |> List.delete(follower_collection) + + {:not_public, length(recipients)} + end + end + + defp object_has_recipients?(%{"object" => object} = activity) do + {_, object_count} = get_recipient_count(object) + {_, activity_count} = get_recipient_count(activity) + object_count + activity_count > 0 + end + + defp object_has_recipients?(object) do + {_, count} = get_recipient_count(object) + count > 0 + end + + @impl true + def filter(%{"type" => "Create", "actor" => actor} = activity) do + with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor), + {:has_mentions, true} <- {:has_mentions, object_has_recipients?(activity)}, + {:good_reputation, true} <- {:good_reputation, good_reputation?(u)} do + {:ok, activity} + else + {:ok, %User{local: true}} -> + {:ok, activity} + + {:has_mentions, false} -> + {:ok, activity} + + {:good_reputation, false} -> + {:reject, "[AntiMentionSpamPolicy] User rejected"} + + {:error, _} -> + {:reject, "[AntiMentionSpamPolicy] Failed to get or fetch user by ap_id"} + + e -> + {:reject, "[AntiMentionSpamPolicy] Unhandled error #{inspect(e)}"} + end + end + + # in all other cases, pass through + def filter(message), do: {:ok, message} + + @impl true + def describe, do: {:ok, %{}} +end -- cgit v1.2.3 From 64cacc3694c0441d3f3f5886b301bbf93f590cb6 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 21 May 2023 19:31:56 -0500 Subject: AntiMentionSpamPolicy: fix user age check --- lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex index ad97a1552..0cb3313b2 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex @@ -12,9 +12,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do defp user_has_posted?(%User{} = u), do: u.note_count > 0 defp user_has_age?(%User{} = u) do - now = NaiveDateTime.utc_now() - diff = u.inserted_at |> NaiveDateTime.diff(now, :second) - diff > :timer.seconds(30) + diff = NaiveDateTime.utc_now() |> NaiveDateTime.diff(u.inserted_at, :second) + diff >= :timer.seconds(30) end defp good_reputation?(%User{} = u) do -- cgit v1.2.3 From 02d8ce8f0ba615fa0946064052113fb05dd0b6a2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 22 May 2023 15:50:34 -0500 Subject: AntiMentionSpamPolicy: remove followers check --- lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex index 0cb3313b2..9cdb2077f 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy - defp user_has_followers?(%User{} = u), do: u.follower_count > 0 defp user_has_posted?(%User{} = u), do: u.note_count > 0 defp user_has_age?(%User{} = u) do @@ -17,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do end defp good_reputation?(%User{} = u) do - user_has_age?(u) and user_has_followers?(u) and user_has_posted?(u) + user_has_age?(u) and user_has_posted?(u) end # copied from HellthreadPolicy -- cgit v1.2.3 From cab6372d7a1bdf50436eff1b4023fd6e05586dbc Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 12:31:29 -0400 Subject: Make user age limit configurable Switch to milliseconds for consistency with other configuration options in codebase --- lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex index 9cdb2077f..531e75ce8 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_mention_spam_policy.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do + alias Pleroma.Config alias Pleroma.User require Pleroma.Constants @@ -11,8 +12,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do defp user_has_posted?(%User{} = u), do: u.note_count > 0 defp user_has_age?(%User{} = u) do - diff = NaiveDateTime.utc_now() |> NaiveDateTime.diff(u.inserted_at, :second) - diff >= :timer.seconds(30) + user_age_limit = Config.get([:mrf_antimentionspam, :user_age_limit], 30_000) + diff = NaiveDateTime.utc_now() |> NaiveDateTime.diff(u.inserted_at, :millisecond) + diff >= user_age_limit end defp good_reputation?(%User{} = u) do -- cgit v1.2.3 From 1c699144d23aa4a86ff8b6ebef7d760ce9e3a4e2 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 21:26:40 +0400 Subject: HttpSecurityPlug: Don't allow unsafe-eval by default --- lib/pleroma/application.ex | 7 ++++- lib/pleroma/web/plugs/http_security_plug.ex | 49 ++++++++++++++++------------- 2 files changed, 33 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d266d1836..0d9757b44 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Application do @name Mix.Project.config()[:name] @version Mix.Project.config()[:version] @repository Mix.Project.config()[:source_url] + @compile_env Mix.env() def name, do: @name def version, do: @version @@ -51,7 +52,11 @@ defmodule Pleroma.Application do Pleroma.HTML.compile_scrubbers() Pleroma.Config.Oban.warn() Config.DeprecationWarnings.warn() - Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() + + if @compile_env != :test do + Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() + end + Pleroma.ApplicationRequirements.verify!() load_custom_modules() Pleroma.Docs.JSON.compile() diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index a27dcd0ab..a1dc6c02a 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -3,26 +3,27 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do - alias Pleroma.Config import Plug.Conn require Logger + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + def init(opts), do: opts def call(conn, _options) do - if Config.get([:http_security, :enabled]) do + if @config_impl.get([:http_security, :enabled]) do conn |> merge_resp_headers(headers()) - |> maybe_send_sts_header(Config.get([:http_security, :sts])) + |> maybe_send_sts_header(@config_impl.get([:http_security, :sts])) else conn end end def primary_frontend do - with %{"name" => frontend} <- Config.get([:frontends, :primary]), - available <- Config.get([:frontends, :available]), + with %{"name" => frontend} <- @config_impl.get([:frontends, :primary]), + available <- @config_impl.get([:frontends, :available]), %{} = primary_frontend <- Map.get(available, frontend) do {:ok, primary_frontend} end @@ -37,8 +38,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do end def headers do - referrer_policy = Config.get([:http_security, :referrer_policy]) - report_uri = Config.get([:http_security, :report_uri]) + referrer_policy = @config_impl.get([:http_security, :referrer_policy]) + report_uri = @config_impl.get([:http_security, :report_uri]) custom_http_frontend_headers = custom_http_frontend_headers() headers = [ @@ -86,10 +87,10 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do @csp_start [Enum.join(static_csp_rules, ";") <> ";"] defp csp_string do - scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] + scheme = @config_impl.get([Pleroma.Web.Endpoint, :url])[:scheme] static_url = Pleroma.Web.Endpoint.static_url() websocket_url = Pleroma.Web.Endpoint.websocket_url() - report_uri = Config.get([:http_security, :report_uri]) + report_uri = @config_impl.get([:http_security, :report_uri]) img_src = "img-src 'self' data: blob:" media_src = "media-src 'self'" @@ -97,8 +98,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do # Strict multimedia CSP enforcement only when MediaProxy is enabled {img_src, media_src, connect_src} = - if Config.get([:media_proxy, :enabled]) && - !Config.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do + if @config_impl.get([:media_proxy, :enabled]) && + !@config_impl.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do sources = build_csp_multimedia_source_list() { @@ -115,17 +116,21 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do end connect_src = - if Config.get(:env) == :dev do + if @config_impl.get(:env) == :dev do [connect_src, " http://localhost:3035/"] else connect_src end script_src = - if Config.get(:env) == :dev do - "script-src 'self' 'unsafe-eval'" + if @config_impl.get([:http_security, :allow_unsafe_eval]) do + if @config_impl.get(:env) == :dev do + "script-src 'self' 'unsafe-eval'" + else + "script-src 'self' 'wasm-unsafe-eval'" + end else - "script-src 'self' 'wasm-unsafe-eval'" + "script-src 'self'" end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] @@ -161,11 +166,11 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do defp build_csp_multimedia_source_list do media_proxy_whitelist = [:media_proxy, :whitelist] - |> Config.get() + |> @config_impl.get() |> build_csp_from_whitelist([]) - captcha_method = Config.get([Pleroma.Captcha, :method]) - captcha_endpoint = Config.get([captcha_method, :endpoint]) + captcha_method = @config_impl.get([Pleroma.Captcha, :method]) + captcha_endpoint = @config_impl.get([captcha_method, :endpoint]) base_endpoints = [ @@ -173,7 +178,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do [Pleroma.Upload, :base_url], [Pleroma.Uploaders.S3, :public_endpoint] ] - |> Enum.map(&Config.get/1) + |> Enum.map(&@config_impl.get/1) [captcha_endpoint | base_endpoints] |> Enum.map(&build_csp_param/1) @@ -200,7 +205,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do end def warn_if_disabled do - unless Config.get([:http_security, :enabled]) do + unless Pleroma.Config.get([:http_security, :enabled]) do Logger.warning(" .i;;;;i. iYcviii;vXY: @@ -245,8 +250,8 @@ your instance and your users via malicious posts: end defp maybe_send_sts_header(conn, true) do - max_age_sts = Config.get([:http_security, :sts_max_age]) - max_age_ct = Config.get([:http_security, :ct_max_age]) + max_age_sts = @config_impl.get([:http_security, :sts_max_age]) + max_age_ct = @config_impl.get([:http_security, :ct_max_age]) merge_resp_headers(conn, [ {"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains"}, -- cgit v1.2.3 From 0847d9ebafa38007aeef0a6677588211994ab546 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 14:35:30 +0000 Subject: Oban queue simplification --- lib/pleroma/scheduled_activity.ex | 2 +- lib/pleroma/web/federator.ex | 2 +- lib/pleroma/workers/attachments_cleanup_worker.ex | 2 +- lib/pleroma/workers/backup_worker.ex | 2 +- lib/pleroma/workers/cron/new_users_digest_worker.ex | 2 +- lib/pleroma/workers/mailer_worker.ex | 2 +- lib/pleroma/workers/mute_expire_worker.ex | 2 +- lib/pleroma/workers/poll_worker.ex | 2 +- lib/pleroma/workers/purge_expired_activity.ex | 4 ++-- lib/pleroma/workers/purge_expired_filter.ex | 4 ++-- lib/pleroma/workers/purge_expired_token.ex | 2 +- lib/pleroma/workers/remote_fetcher_worker.ex | 2 +- lib/pleroma/workers/rich_media_expiration_worker.ex | 2 +- lib/pleroma/workers/scheduled_activity_worker.ex | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 63c6cb45b..c361d7d89 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -204,7 +204,7 @@ defmodule Pleroma.ScheduledActivity do def job_query(scheduled_activity_id) do from(j in Oban.Job, - where: j.queue == "scheduled_activities", + where: j.queue == "federator_outgoing", where: fragment("args ->> 'activity_id' = ?::text", ^to_string(scheduled_activity_id)) ) end diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 1f2c3835a..4b30fd21d 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -44,7 +44,7 @@ defmodule Pleroma.Web.Federator do end def incoming_ap_doc(%{"type" => "Delete"} = params) do - ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}, priority: 3) + ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}, priority: 3, queue: :slow) end def incoming_ap_doc(params) do diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index 4c1764053..0b570b70b 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do alias Pleroma.Object alias Pleroma.Repo - use Pleroma.Workers.WorkerHelper, queue: "attachments_cleanup" + use Pleroma.Workers.WorkerHelper, queue: "slow" @impl Oban.Worker def perform(%Job{ diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex index a485ddb4b..54ac31a3c 100644 --- a/lib/pleroma/workers/backup_worker.ex +++ b/lib/pleroma/workers/backup_worker.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackupWorker do - use Oban.Worker, queue: :backup, max_attempts: 1 + use Oban.Worker, queue: :slow, max_attempts: 1 alias Oban.Job alias Pleroma.User.Backup diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index 1c3e445aa..d2abb2d3b 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do import Ecto.Query - use Pleroma.Workers.WorkerHelper, queue: "mailer" + use Pleroma.Workers.WorkerHelper, queue: "background" @impl Oban.Worker def perform(_job) do diff --git a/lib/pleroma/workers/mailer_worker.ex b/lib/pleroma/workers/mailer_worker.ex index 940716558..652bf77e0 100644 --- a/lib/pleroma/workers/mailer_worker.ex +++ b/lib/pleroma/workers/mailer_worker.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MailerWorker do - use Pleroma.Workers.WorkerHelper, queue: "mailer" + use Pleroma.Workers.WorkerHelper, queue: "background" @impl Oban.Worker def perform(%Job{args: %{"op" => "email", "encoded_email" => encoded_email, "config" => config}}) do diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index 8ce458d48..8ad287a7f 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MuteExpireWorker do - use Pleroma.Workers.WorkerHelper, queue: "mute_expire" + use Pleroma.Workers.WorkerHelper, queue: "background" @impl Oban.Worker def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 022d026f8..70df54193 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Workers.PollWorker do @moduledoc """ Generates notifications when a poll ends. """ - use Pleroma.Workers.WorkerHelper, queue: "poll_notifications" + use Pleroma.Workers.WorkerHelper, queue: "background" alias Pleroma.Activity alias Pleroma.Notification diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index e554684fe..a65593b6e 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do Worker which purges expired activity. """ - use Oban.Worker, queue: :activity_expiration, max_attempts: 1, unique: [period: :infinity] + use Oban.Worker, queue: :slow, max_attempts: 1, unique: [period: :infinity] import Ecto.Query @@ -59,7 +59,7 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do def get_expiration(id) do from(j in Oban.Job, where: j.state == "scheduled", - where: j.queue == "activity_expiration", + where: j.queue == "slow", where: fragment("?->>'activity_id' = ?", j.args, ^id) ) |> Pleroma.Repo.one() diff --git a/lib/pleroma/workers/purge_expired_filter.ex b/lib/pleroma/workers/purge_expired_filter.ex index 9114aeb7f..1f6931e4c 100644 --- a/lib/pleroma/workers/purge_expired_filter.ex +++ b/lib/pleroma/workers/purge_expired_filter.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.PurgeExpiredFilter do Worker which purges expired filters """ - use Oban.Worker, queue: :filter_expiration, max_attempts: 1, unique: [period: :infinity] + use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: :infinity] import Ecto.Query @@ -38,7 +38,7 @@ defmodule Pleroma.Workers.PurgeExpiredFilter do def get_expiration(id) do from(j in Job, where: j.state == "scheduled", - where: j.queue == "filter_expiration", + where: j.queue == "background", where: fragment("?->'filter_id' = ?", j.args, ^id) ) |> Repo.one() diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex index 2ccd9e80b..1854bf561 100644 --- a/lib/pleroma/workers/purge_expired_token.ex +++ b/lib/pleroma/workers/purge_expired_token.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.PurgeExpiredToken do Worker which purges expired OAuth tokens """ - use Oban.Worker, queue: :token_expiration, max_attempts: 1 + use Oban.Worker, queue: :background, max_attempts: 1 @spec enqueue(%{token_id: integer(), valid_until: DateTime.t(), mod: module()}) :: {:ok, Oban.Job.t()} | {:error, Ecto.Changeset.t()} diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index c26418483..ed04c54b2 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do alias Pleroma.Object.Fetcher - use Pleroma.Workers.WorkerHelper, queue: "remote_fetcher" + use Pleroma.Workers.WorkerHelper, queue: "background" @impl Oban.Worker def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do diff --git a/lib/pleroma/workers/rich_media_expiration_worker.ex b/lib/pleroma/workers/rich_media_expiration_worker.ex index d7ae497a7..0b74687cf 100644 --- a/lib/pleroma/workers/rich_media_expiration_worker.ex +++ b/lib/pleroma/workers/rich_media_expiration_worker.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Workers.RichMediaExpirationWorker do alias Pleroma.Web.RichMedia.Card use Oban.Worker, - queue: :rich_media_expiration + queue: :background @impl Oban.Worker def perform(%Job{args: %{"url" => url} = _args}) do diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index 4df84d00f..ab62686f4 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do The worker to post scheduled activity. """ - use Pleroma.Workers.WorkerHelper, queue: "scheduled_activities" + use Pleroma.Workers.WorkerHelper, queue: "federator_outgoing" alias Pleroma.Repo alias Pleroma.ScheduledActivity -- cgit v1.2.3 From 6b8c15a4a1fdbc2a2a4ef194d9519e717470c632 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 14:11:42 -0400 Subject: Remove MediaProxyWarmingPolicy config for ConcurrentLimiter as we are not using it --- lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index e5eb6896a..0c5b53def 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -22,11 +22,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do Logger.debug("Prefetching #{inspect(url)} as #{inspect(prefetch_url)}") - if Pleroma.Config.get(:env) == :test do - fetch(prefetch_url) - else - Task.start(fn -> fetch(prefetch_url) end) - end + fetch(prefetch_url) end end -- cgit v1.2.3 From 81e44ced0c7251b5a6b585f297e1e00fad08c6d1 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Mon, 27 May 2024 22:13:20 +0400 Subject: HTTPSecurityPlug: Fix tests --- lib/pleroma/web/plugs/http_security_plug.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index a1dc6c02a..38f6c511e 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -116,7 +116,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do end connect_src = - if @config_impl.get(:env) == :dev do + if @config_impl.get([:env]) == :dev do [connect_src, " http://localhost:3035/"] else connect_src @@ -124,7 +124,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do script_src = if @config_impl.get([:http_security, :allow_unsafe_eval]) do - if @config_impl.get(:env) == :dev do + if @config_impl.get([:env]) == :dev do "script-src 'self' 'unsafe-eval'" else "script-src 'self' 'wasm-unsafe-eval'" -- cgit v1.2.3 From bb86a01b9b5889155f60f08143755dd101d925f0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 27 May 2024 15:20:47 -0400 Subject: Credo --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index d2b2cae0b..e6161455d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -522,7 +522,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do conn end - defp log_inbox_metadata(conn = %{params: %{"actor" => actor, "type" => type}}, _) do + defp log_inbox_metadata(%{params: %{"actor" => actor, "type" => type}} = conn, _) do Logger.metadata(actor: actor, type: type) conn end -- cgit v1.2.3 From 73d58c22d4a9a87539cf1c3a33083464fc4b8540 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 28 May 2024 08:09:19 +0400 Subject: Linting --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index d2b2cae0b..e6161455d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -522,7 +522,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do conn end - defp log_inbox_metadata(conn = %{params: %{"actor" => actor, "type" => type}}, _) do + defp log_inbox_metadata(%{params: %{"actor" => actor, "type" => type}} = conn, _) do Logger.metadata(actor: actor, type: type) conn end -- cgit v1.2.3 From f5978da67633110acbc6494ff6f07b3f07424779 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Tue, 28 May 2024 14:00:25 +0400 Subject: HTTPSignaturePlugTest: Rewrite to use mox. --- lib/pleroma/http_signatures_api.ex | 4 ++++ lib/pleroma/web/plugs/http_signature_plug.ex | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/http_signatures_api.ex (limited to 'lib') diff --git a/lib/pleroma/http_signatures_api.ex b/lib/pleroma/http_signatures_api.ex new file mode 100644 index 000000000..8e73dc98e --- /dev/null +++ b/lib/pleroma/http_signatures_api.ex @@ -0,0 +1,4 @@ +defmodule Pleroma.HTTPSignaturesAPI do + @callback validate_conn(conn :: Plug.Conn.t()) :: boolean + @callback signature_for_conn(conn :: Plug.Conn.t()) :: map +end diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index 71b2a5f51..6bf2dd432 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -8,11 +8,17 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do import Plug.Conn import Phoenix.Controller, only: [get_format: 1, text: 2] - alias Pleroma.Config alias Pleroma.Web.ActivityPub.MRF require Logger + @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) + @http_signatures_impl Application.compile_env( + :pleroma, + [__MODULE__, :http_signatures_impl], + HTTPSignatures + ) + def init(options) do options end @@ -41,7 +47,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do |> put_req_header("(request-target)", request_target) |> put_req_header("@request-target", request_target) - HTTPSignatures.validate_conn(conn) + @http_signatures_impl.validate_conn(conn) end defp validate_signature(conn) do @@ -108,9 +114,9 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do - if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do + if @config_impl.get([:activitypub, :authorized_fetch_mode], false) do exceptions = - Pleroma.Config.get([:activitypub, :authorized_fetch_mode_exceptions], []) + @config_impl.get([:activitypub, :authorized_fetch_mode_exceptions], []) |> Enum.map(&InetHelper.parse_cidr/1) if Enum.any?(exceptions, fn x -> InetCidr.contains?(x, remote_ip) end) do @@ -129,7 +135,8 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defp maybe_filter_requests(%{halted: true} = conn), do: conn defp maybe_filter_requests(conn) do - if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do + if @config_impl.get([:activitypub, :authorized_fetch_mode], false) and + conn.assigns[:actor_id] do %{host: host} = URI.parse(conn.assigns.actor_id) if MRF.subdomain_match?(rejected_domains(), host) do @@ -145,7 +152,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end defp rejected_domains do - Config.get([:instance, :rejected_instances]) + @config_impl.get([:instance, :rejected_instances]) |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() end -- cgit v1.2.3 From 0b864c3696e47ba1def6047905dad9065b0bee0e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 08:49:34 -0400 Subject: Dialyzer: fix invalid @spec lib/pleroma/notification.ex:492:invalid_contract The @spec for the function does not match the success typing of the function. Function: Pleroma.Notification.get_notified_from_activity/2 Success typing: @spec get_notified_from_activity(_, _) :: [any()] --- lib/pleroma/notification.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 4f714b25f..f521a2998 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -489,7 +489,7 @@ defmodule Pleroma.Notification do NOTE: might be called for FAKE Activities, see ActivityPub.Utils.get_notified_from_object/1 """ - @spec get_notified_from_activity(Activity.t(), boolean()) :: {list(User.t()), list(User.t())} + @spec get_notified_from_activity(Activity.t(), boolean()) :: list(User.t()) def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) -- cgit v1.2.3 From 42c5f7c74e93b7b489456578f8285d06320c15dc Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 08:55:18 -0400 Subject: Dialyzer: fix invalid @spec The callback already defines the @spec and these do not match it. lib/pleroma/upload/filter/exiftool/strip_location.ex:12:callback_spec_type_mismatch The @spec return type does not match the expected return type for filter/1 callback in Pleroma.Upload.Filter behaviour. Actual: @spec filter(...) :: {:ok, _} Expected: @spec filter(...) :: {:error, _} | {:ok, :filtered | :noop} | {:ok, :filtered, struct()} --- lib/pleroma/upload/filter/exiftool/strip_location.ex | 2 -- lib/pleroma/upload/filter/mogrifun.ex | 1 - lib/pleroma/upload/filter/mogrify.ex | 1 - 3 files changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload/filter/exiftool/strip_location.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex index f2bcc4622..8becee712 100644 --- a/lib/pleroma/upload/filter/exiftool/strip_location.ex +++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex @@ -9,8 +9,6 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do """ @behaviour Pleroma.Upload.Filter - @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - # Formats not compatible with exiftool at this time def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex index a0f247b70..9716580a8 100644 --- a/lib/pleroma/upload/filter/mogrifun.ex +++ b/lib/pleroma/upload/filter/mogrifun.ex @@ -38,7 +38,6 @@ defmodule Pleroma.Upload.Filter.Mogrifun do [{"fill", "yellow"}, {"tint", "40"}] ] - @spec filter(Pleroma.Upload.t()) :: {:ok, atom()} | {:error, String.t()} def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do try do Filter.Mogrify.do_filter(file, [Enum.random(@filters)]) diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex index 06efbf321..d1e166022 100644 --- a/lib/pleroma/upload/filter/mogrify.ex +++ b/lib/pleroma/upload/filter/mogrify.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Upload.Filter.Mogrify do @type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()} @type conversions :: conversion() | [conversion()] - @spec filter(Pleroma.Upload.t()) :: {:ok, :atom} | {:error, String.t()} def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do try do do_filter(file, Pleroma.Config.get!([__MODULE__, :args])) -- cgit v1.2.3 From f8ce639e3f76257097793c666d3ebf8f22539a30 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 09:30:19 -0400 Subject: Dialyzer: guard clause can never succeed lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex:106:guard_fail The guard clause: when _ :: [ binary() | [string() | char()] | {string() | integer(), string()} | {{byte(), byte(), byte(), byte()}, integer(), binary()} | {integer(), integer(), integer(), string() | byte()} | {integer(), integer(), string(), string(), string(), string()} | {string(), string(), integer(), integer(), integer(), integer(), integer()} | {char(), char(), char(), char(), char(), char(), char(), char()} ] === nil can never succeed. --- lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex b/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex index 9543cc545..7c6bb888f 100644 --- a/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/dnsrbl_policy.ex @@ -103,7 +103,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do {:ok, object} else Task.start(fn -> - reason = rblquery(query, :txt) || "undefined" + reason = + case rblquery(query, :txt) do + [[result]] -> result + _ -> "undefined" + end Logger.warning( "DNSRBL Rejected activity from #{actor_host} for reason: #{inspect(reason)}" -- cgit v1.2.3 From 18835bf7012e8e234eb27456a437f4d1e8796645 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 09:38:36 -0400 Subject: Use the configured http client options for mediaproxy --- lib/pleroma/helpers/media_helper.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index e44114d9d..0ac07fa41 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Helpers.MediaHelper do end def image_resize(url, options) do - with {:ok, env} <- HTTP.get(url, [], pool: :media), + with {:ok, env} <- HTTP.get(url, [], http_client_opts()), {:ok, resized} <- Operation.thumbnail_buffer(env.body, options.max_width, height: options.max_height, @@ -46,7 +46,7 @@ defmodule Pleroma.Helpers.MediaHelper do def video_framegrab(url) do with executable when is_binary(executable) <- System.find_executable("ffmpeg"), false <- @cachex.exists?(:failed_media_helper_cache, url), - {:ok, env} <- HTTP.get(url, [], pool: :media), + {:ok, env} <- HTTP.get(url, [], http_client_opts()), {:ok, pid} <- StringIO.open(env.body) do body_stream = IO.binstream(pid, 1) @@ -84,4 +84,6 @@ defmodule Pleroma.Helpers.MediaHelper do {:error, _} = error -> error end end + + defp http_client_opts, do: Pleroma.Config.get([:media_proxy, :proxy_opts, :http], pool: :media) end -- cgit v1.2.3 From 17ebb2df8404474ef66c6a38e974143166b5e49b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 09:43:35 -0400 Subject: Dialyzer: fix pattern matches preventing video thumbnailing from working lib/pleroma/web/media_proxy/media_proxy_controller.ex:154:pattern_match The pattern can never match the type. Pattern: {:ok, _thumbnail_binary} Type: {:error, boolean() | {:ffmpeg, :command_not_found}} --- lib/pleroma/helpers/media_helper.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 0ac07fa41..8566ab3ea 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -45,7 +45,7 @@ defmodule Pleroma.Helpers.MediaHelper do @spec video_framegrab(String.t()) :: {:ok, binary()} | {:error, any()} def video_framegrab(url) do with executable when is_binary(executable) <- System.find_executable("ffmpeg"), - false <- @cachex.exists?(:failed_media_helper_cache, url), + {:ok, false} <- @cachex.exists?(:failed_media_helper_cache, url), {:ok, env} <- HTTP.get(url, [], http_client_opts()), {:ok, pid} <- StringIO.open(env.body) do body_stream = IO.binstream(pid, 1) @@ -71,13 +71,13 @@ defmodule Pleroma.Helpers.MediaHelper do end) case Task.yield(task, 5_000) do - nil -> + {:ok, result} -> + {:ok, result} + + _ -> Task.shutdown(task) @cachex.put(:failed_media_helper_cache, url, nil) {:error, {:ffmpeg, :timeout}} - - result -> - {:ok, result} end else nil -> {:error, {:ffmpeg, :command_not_found}} -- cgit v1.2.3 From 1b3c84e241f8ed1066d113346365ce489971ac14 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 09:58:44 -0400 Subject: Dialyzer: no_local_return WebPushEncryption.send_web_push/4 was written to raise on erroroneus input, so we must guard against that. lib/pleroma/web/push/impl.ex:65:no_return Function push_message/4 has no local return. --- lib/pleroma/web/push/impl.ex | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 9e68d827b..53334e72c 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -63,19 +63,25 @@ defmodule Pleroma.Web.Push.Impl do @doc "Push message to web" def push_message(body, sub, api_key, subscription) do - case WebPushEncryption.send_web_push(body, sub, api_key) do - {:ok, %{status: code}} when code in 400..499 -> - Logger.debug("Removing subscription record") - Repo.delete!(subscription) - :ok - - {:ok, %{status: code}} when code in 200..299 -> - :ok - - {:ok, %{status: code}} -> - Logger.error("Web Push Notification failed with code: #{code}") - :error - + try do + case WebPushEncryption.send_web_push(body, sub, api_key) do + {:ok, %{status: code}} when code in 400..499 -> + Logger.debug("Removing subscription record") + Repo.delete!(subscription) + :ok + + {:ok, %{status: code}} when code in 200..299 -> + :ok + + {:ok, %{status: code}} -> + Logger.error("Web Push Notification failed with code: #{code}") + :error + + error -> + Logger.error("Web Push Notification failed with #{inspect(error)}") + :error + end + rescue error -> Logger.error("Web Push Notification failed with #{inspect(error)}") :error -- cgit v1.2.3 From 8743c6c640d395ff6d7d268df1e382ba0fb0ca96 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 10:36:00 -0400 Subject: Dialyzer: The pattern can never match the type We will never pass :plain to query_with/4, so remove that match and change it to query_with/3 lib/pleroma/search/database_search.ex:127:pattern_match The pattern can never match the type. Pattern: _q, :rum, _search_query, :plain Type: %Ecto.Query{ :aliases => _, :assocs => _, :combinations => _, :distinct => _, :from => _, :group_bys => _, :havings => _, :joins => _, :limit => _, :lock => _, :offset => _, :order_bys => _, :prefix => _, :preloads => _, :select => _, :sources => _, :updates => _, :wheres => _, :windows => _, :with_ctes => _ }, :rum, _, :websearch --- lib/pleroma/search/database_search.ex | 36 +++-------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search/database_search.ex b/lib/pleroma/search/database_search.ex index c6fe8a9bd..aef5d1e74 100644 --- a/lib/pleroma/search/database_search.ex +++ b/lib/pleroma/search/database_search.ex @@ -28,7 +28,7 @@ defmodule Pleroma.Search.DatabaseSearch do |> Activity.with_preloaded_object() |> Activity.restrict_deactivated_users() |> restrict_public(user) - |> query_with(index_type, search_query, :websearch) + |> query_with(index_type, search_query) |> maybe_restrict_local(user) |> maybe_restrict_author(author) |> maybe_restrict_blocked(user) @@ -88,25 +88,7 @@ defmodule Pleroma.Search.DatabaseSearch do ) end - defp query_with(q, :gin, search_query, :plain) do - %{rows: [[tsc]]} = - Ecto.Adapters.SQL.query!( - Pleroma.Repo, - "select current_setting('default_text_search_config')::regconfig::oid;" - ) - - from([a, o] in q, - where: - fragment( - "to_tsvector(?::oid::regconfig, ?->>'content') @@ plainto_tsquery(?)", - ^tsc, - o.data, - ^search_query - ) - ) - end - - defp query_with(q, :gin, search_query, :websearch) do + defp query_with(q, :gin, search_query) do %{rows: [[tsc]]} = Ecto.Adapters.SQL.query!( Pleroma.Repo, @@ -124,19 +106,7 @@ defmodule Pleroma.Search.DatabaseSearch do ) end - defp query_with(q, :rum, search_query, :plain) do - from([a, o] in q, - where: - fragment( - "? @@ plainto_tsquery(?)", - o.fts_content, - ^search_query - ), - order_by: [fragment("? <=> now()::date", o.inserted_at)] - ) - end - - defp query_with(q, :rum, search_query, :websearch) do + defp query_with(q, :rum, search_query) do from([a, o] in q, where: fragment( -- cgit v1.2.3 From 6551ca2db7a0907252bbc649c7d082b3edf92a93 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 10:40:54 -0400 Subject: Dialyzer: overlapping_contract Wrong @spec name for remove_from_block/2 lib/pleroma/user.ex:2721:overlapping_contract Overloaded contract for Pleroma.User.add_to_block/2 has overlapping domains; such contracts are currently unsupported and are simply ignored. --- lib/pleroma/user.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 6d6aa98b5..d94b68ce0 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2727,7 +2727,7 @@ defmodule Pleroma.User do end end - @spec add_to_block(User.t(), User.t()) :: + @spec remove_from_block(User.t(), User.t()) :: {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()} defp remove_from_block(%User{} = user, %User{} = blocked) do with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do -- cgit v1.2.3 From 6b6a2adb07c3b9a52cd0a5adf435a916088bb4d7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 10:49:43 -0400 Subject: Dialyzer: The function call will not succeed. :idna.encode/1 expects a charlist even though it will accept a binary string. That functionality is undocumented / not part of its typespec, so we should turn it into a charlist first. Also switch to using match?/2 lib/pleroma/user.ex:2056:call The function call will not succeed. :idna.encode(_host :: binary()) will never return since the success typing is: (string()) :: string() and the contract is (string()) :: string() --- lib/pleroma/user.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d94b68ce0..884c1f302 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2053,7 +2053,8 @@ defmodule Pleroma.User do %{scheme: scheme, userinfo: nil, host: host} when not_empty_string(host) and scheme in ["http", "https"] <- URI.parse(value), - {:not_idn, true} <- {:not_idn, to_string(:idna.encode(host)) == host}, + {:not_idn, true} <- + {:not_idn, match?(^host, to_string(:idna.encode(to_charlist(host))))}, "me" <- Pleroma.Web.RelMe.maybe_put_rel_me(value, profile_urls) do CommonUtils.to_masto_date(NaiveDateTime.utc_now()) else -- cgit v1.2.3 From 79c418bcb7534ae3645ee0b7728f8e65a031f7a4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 11:07:28 -0400 Subject: Dialyzer: fix invalid @spec --- lib/pleroma/web/o_auth/token.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex index a5ad2e909..9b1198b42 100644 --- a/lib/pleroma/web/o_auth/token.ex +++ b/lib/pleroma/web/o_auth/token.ex @@ -96,7 +96,7 @@ defmodule Pleroma.Web.OAuth.Token do |> validate_required([:valid_until]) end - @spec create(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Ecto.Changeset.t()} + @spec create(App.t(), User.t(), map()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()} def create(%App{} = app, %User{} = user, attrs \\ %{}) do with {:ok, token} <- do_create(app, user, attrs) do if Pleroma.Config.get([:oauth2, :clean_expired_tokens]) do -- cgit v1.2.3 From c16ef40f134e69ad509a52dd3470672ca297f084 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 29 May 2024 07:59:02 +0200 Subject: RichMedia: Respect configuration on status previews --- lib/pleroma/web/rich_media/card.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/card.ex b/lib/pleroma/web/rich_media/card.ex index 36a1ae44a..f49bb2fee 100644 --- a/lib/pleroma/web/rich_media/card.ex +++ b/lib/pleroma/web/rich_media/card.ex @@ -104,7 +104,8 @@ defmodule Pleroma.Web.RichMedia.Card do @spec get_by_activity(Activity.t()) :: t() | nil | :error # Fake/Draft activity def get_by_activity(%Activity{id: "pleroma:fakeid"} = activity) do - with %Object{} = object <- Object.normalize(activity, fetch: false), + with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])}, + %Object{} = object <- Object.normalize(activity, fetch: false), url when not is_nil(url) <- HTML.extract_first_external_url_from_object(object) do case get_by_url(url) do # Cache hit -- cgit v1.2.3 From 65c8763907e28a12bc0a4c1269b5a765006d65b2 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 29 May 2024 08:00:19 +0200 Subject: RichMedia: Add extra checks on configuration --- lib/pleroma/web/rich_media/card.ex | 22 +++++++++++++--------- lib/pleroma/web/rich_media/parser.ex | 6 +++++- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/card.ex b/lib/pleroma/web/rich_media/card.ex index f49bb2fee..040066f36 100644 --- a/lib/pleroma/web/rich_media/card.ex +++ b/lib/pleroma/web/rich_media/card.ex @@ -77,19 +77,23 @@ defmodule Pleroma.Web.RichMedia.Card do @spec get_or_backfill_by_url(String.t(), map()) :: t() | nil def get_or_backfill_by_url(url, backfill_opts \\ %{}) do - case get_by_url(url) do - %__MODULE__{} = card -> - card + if @config_impl.get([:rich_media, :enabled]) do + case get_by_url(url) do + %__MODULE__{} = card -> + card - nil -> - backfill_opts = Map.put(backfill_opts, :url, url) + nil -> + backfill_opts = Map.put(backfill_opts, :url, url) - Backfill.start(backfill_opts) + Backfill.start(backfill_opts) - nil + nil - :error -> - nil + :error -> + nil + end + else + nil end end diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 37cf29029..7f6b5d388 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -15,10 +15,14 @@ defmodule Pleroma.Web.RichMedia.Parser do @spec parse(String.t()) :: {:ok, map()} | {:error, any()} def parse(url) do - with :ok <- validate_page_url(url), + with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])}, + :ok <- validate_page_url(url), {:ok, data} <- parse_url(url) do data = Map.put(data, "url", url) {:ok, data} + else + {:config, _} -> {:error, :rich_media_disabled} + e -> e end end -- cgit v1.2.3 From 14b4bd69a83846b3c117624851b96b9fc30528bf Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 29 May 2024 10:44:34 -0400 Subject: Add additional flags to the Pleroma.Search.Indexer Mix task --- lib/mix/tasks/pleroma/search/indexer.ex | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/search/indexer.ex b/lib/mix/tasks/pleroma/search/indexer.ex index 81a9fced6..2a52472f9 100644 --- a/lib/mix/tasks/pleroma/search/indexer.ex +++ b/lib/mix/tasks/pleroma/search/indexer.ex @@ -33,15 +33,18 @@ defmodule Mix.Tasks.Pleroma.Search.Indexer do OptionParser.parse( options, strict: [ - limit: :integer + chunk: :integer, + limit: :integer, + step: :integer ] ) start_pleroma() + chunk_size = Keyword.get(options, :chunk, 100) limit = Keyword.get(options, :limit, 100_000) + per_step = Keyword.get(options, :step, 1000) - per_step = 1000 chunks = max(div(limit, per_step), 1) 1..chunks @@ -65,7 +68,7 @@ defmodule Mix.Tasks.Pleroma.Search.Indexer do IO.puts("Got #{length(ids)} activities, adding to indexer") ids - |> Enum.chunk_every(100) + |> Enum.chunk_every(chunk_size) |> Enum.each(fn chunk -> IO.puts("Adding #{length(chunk)} activities to indexing queue") -- cgit v1.2.3 From f5065eaf99a76695df26966055f90368df7043f3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 30 May 2024 11:09:42 -0400 Subject: Fix Logger.warn deprecation error on OTP25 --- lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index f7863039b..3d1c273b9 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -64,7 +64,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do Jason.decode(body) else error -> - Logger.warn(""" + Logger.warning(""" [NsfwApiPolicy]: The API server failed. Skipping. #{inspect(error)} """) -- cgit v1.2.3 From cfc8d7aade526b8f119683984977064cd3cd3d87 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 28 May 2024 13:14:34 -0400 Subject: IPFS uploader: dialyzer fixes lib/pleroma/uploaders/ipfs.ex:43:no_return Function put_file/1 has no local return. ________________________________________________________________________________ lib/pleroma/uploaders/ipfs.ex:49:call The function call will not succeed. Pleroma.HTTP.post( binary(), _mp :: %Tesla.Multipart{ :boundary => binary(), :content_type_params => [binary()], :parts => [ %Tesla.Multipart.Part{ :body => binary(), :dispositions => [any()], :headers => [any()] }, ... ] }, [], [{:params, [{:"cid-version", <<49>>}]}] ) will never return since the success typing is: (binary(), binary(), [{binary(), binary()}], Keyword.t()) :: {:error, _} | {:ok, %Tesla.Env{ :__client__ => %Tesla.Client{ :adapter => nil | {_, _} | {_, _, _}, :fun => _, :post => [any()], :pre => [any()] }, :__module__ => atom(), :body => _, :headers => [{_, _}], :method => :delete | :get | :head | :options | :patch | :post | :put | :trace, :opts => [{_, _}], :query => [{_, _}], :status => nil | integer(), :url => binary() }} and the contract is (Pleroma.HTTP.Request.url(), String.t(), Pleroma.HTTP.Request.headers(), :elixir.keyword()) :: {:ok, Tesla.Env.t()} | {:error, any()} --- lib/pleroma/http.ex | 4 +-- lib/pleroma/uploaders/ipfs.ex | 57 ++++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex index eec61cf14..ec837e509 100644 --- a/lib/pleroma/http.ex +++ b/lib/pleroma/http.ex @@ -37,7 +37,7 @@ defmodule Pleroma.HTTP do See `Pleroma.HTTP.request/5` """ - @spec post(Request.url(), String.t(), Request.headers(), keyword()) :: + @spec post(Request.url(), Tesla.Env.body(), Request.headers(), keyword()) :: {:ok, Env.t()} | {:error, any()} def post(url, body, headers \\ [], options \\ []), do: request(:post, url, body, headers, options) @@ -56,7 +56,7 @@ defmodule Pleroma.HTTP do `{:ok, %Tesla.Env{}}` or `{:error, error}` """ - @spec request(method(), Request.url(), String.t(), Request.headers(), keyword()) :: + @spec request(method(), Request.url(), Tesla.Env.body(), Request.headers(), keyword()) :: {:ok, Env.t()} | {:error, any()} def request(method, url, body, headers, options) when is_binary(url) do uri = URI.parse(url) diff --git a/lib/pleroma/uploaders/ipfs.ex b/lib/pleroma/uploaders/ipfs.ex index d171e4652..5930a129e 100644 --- a/lib/pleroma/uploaders/ipfs.ex +++ b/lib/pleroma/uploaders/ipfs.ex @@ -8,23 +8,10 @@ defmodule Pleroma.Uploaders.IPFS do alias Tesla.Multipart + @api_add "/api/v0/add" + @api_delete "/api/v0/files/rm" @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) - defp get_final_url(method) do - config = @config_impl.get([__MODULE__]) - post_base_url = Keyword.get(config, :post_gateway_url) - - Path.join([post_base_url, method]) - end - - def put_file_endpoint do - get_final_url("/api/v0/add") - end - - def delete_file_endpoint do - get_final_url("/api/v0/files/rm") - end - @placeholder "{CID}" def placeholder, do: @placeholder @@ -40,26 +27,26 @@ defmodule Pleroma.Uploaders.IPFS do end @impl true - def put_file(%Pleroma.Upload{} = upload) do + def put_file(%Pleroma.Upload{tempfile: tempfile}) do mp = Multipart.new() |> Multipart.add_content_type_param("charset=utf-8") - |> Multipart.add_file(upload.tempfile) + |> Multipart.add_file(tempfile) - case Pleroma.HTTP.post(put_file_endpoint(), mp, [], params: ["cid-version": "1"]) do - {:ok, ret} -> - case Jason.decode(ret.body) do - {:ok, ret} -> - if Map.has_key?(ret, "Hash") do - {:ok, {:file, ret["Hash"]}} - else - {:error, "JSON doesn't contain Hash key"} - end + endpoint = ipfs_endpoint(@api_add) - error -> - Logger.error("#{__MODULE__}: #{inspect(error)}") - {:error, "JSON decode failed"} - end + with {:ok, %{body: body}} when is_binary(body) <- + Pleroma.HTTP.post(endpoint, mp, [], params: ["cid-version": "1"], pool: :upload), + {_, {:ok, decoded}} <- {:json, Jason.decode(body)}, + {_, true} <- {:hash, Map.has_key?(decoded, "Hash")} do + {:ok, {:file, decoded["Hash"]}} + else + {:hash, false} -> + {:error, "JSON doesn't contain Hash key"} + + {:json, error} -> + Logger.error("#{__MODULE__}: #{inspect(error)}") + {:error, "JSON decode failed"} error -> Logger.error("#{__MODULE__}: #{inspect(error)}") @@ -69,9 +56,17 @@ defmodule Pleroma.Uploaders.IPFS do @impl true def delete_file(file) do - case Pleroma.HTTP.post(delete_file_endpoint(), "", [], params: [arg: file]) do + endpoint = ipfs_endpoint(@api_delete) + + case Pleroma.HTTP.post(endpoint, "", [], params: [arg: file]) do {:ok, %{status: 204}} -> :ok error -> {:error, inspect(error)} end end + + defp ipfs_endpoint(path) do + URI.parse(@config_impl.get([__MODULE__, :post_gateway_url])) + |> Map.put(:path, path) + |> URI.to_string() + end end -- cgit v1.2.3 From 0302431888d457d254f152a502946e6ffe7935e4 Mon Sep 17 00:00:00 2001 From: Floatingghost Date: Fri, 31 May 2024 09:04:00 -0400 Subject: Use proper workers for fetching pins instead of an ad-hoc task BUG: https://git.pleroma.social/pleroma/pleroma/-/issues/3276 --- lib/pleroma/web/activity_pub/activity_pub.ex | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5bb0fba6e..1247ae7ce 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1794,24 +1794,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def pinned_fetch_task(nil), do: nil - - def pinned_fetch_task(%{pinned_objects: pins}) do - if Enum.all?(pins, fn {ap_id, _} -> - Object.get_cached_by_ap_id(ap_id) || - match?({:ok, _object}, Fetcher.fetch_object_from_id(ap_id)) - end) do - :ok - else - :error - end + def enqueue_pin_fetches(%{pinned_objects: pins}) do + # enqueue a task to fetch all pinned objects + Enum.each(pins, fn {ap_id, _} -> + if is_nil(Object.get_cached_by_ap_id(ap_id)) do + Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{ + "id" => ap_id, + "depth" => 1 + }) + end + end) end + def enqueue_pin_fetches(_), do: nil + def make_user_from_ap_id(ap_id, additional \\ []) do user = User.get_cached_by_ap_id(ap_id) with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do - {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end) + enqueue_pin_fetches(data) if user do user -- cgit v1.2.3 From 858d528cc1b551e23850a4b65eb43e44a2a18cf4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 4 Jun 2024 22:44:56 -0400 Subject: Allow Cowboy to stream the response instead of chunk it --- lib/pleroma/reverse_proxy.ex | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index 4d13e51fc..8aec4ae58 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do ~w(if-unmodified-since if-none-match) ++ @range_headers @resp_cache_headers ~w(etag date last-modified) @keep_resp_headers @resp_cache_headers ++ - ~w(content-type content-disposition content-encoding) ++ + ~w(content-length content-type content-disposition content-encoding) ++ ~w(content-range accept-ranges vary) @default_cache_control_header "public, max-age=1209600" @valid_resp_codes [200, 206, 304] @@ -180,6 +180,7 @@ defmodule Pleroma.ReverseProxy do result = conn |> put_resp_headers(build_resp_headers(headers, opts)) + |> streaming_compat |> send_chunked(status) |> chunk_reply(client, opts) @@ -417,4 +418,17 @@ defmodule Pleroma.ReverseProxy do @cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl) end + + # When Cowboy handles a chunked response with a content-length header it streams + # over HTTP 1.1 instead of chunking. Bandit cannot stream over HTTP 1.1 so the header + # must be stripped or it breaks RFC compliance for Transfer Encoding: Chunked. RFC9112§6.2 + # + # HTTP2 is always streamed for all adapters. + defp streaming_compat(conn) do + with Phoenix.Endpoint.Cowboy2Adapter <- Pleroma.Web.Endpoint.config(:adapter) do + conn + else + _ -> delete_resp_header(conn, "content-length") + end + end end -- cgit v1.2.3 From 5915062874f0697beb51bd80eabafb6cfa34d362 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 7 Jun 2024 15:50:11 -0400 Subject: Add missing notification types to the api spec --- lib/pleroma/web/api_spec/operations/notification_operation.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index a79eb8f74..2dc0f66df 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -203,7 +203,10 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do "move", "follow_request", "poll", - "status" + "status", + "update", + "admin.sign_up", + "admin.report" ], description: """ The type of event that resulted in the notification. @@ -218,6 +221,9 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do - `pleroma:chat_mention` - Someone mentioned you in a chat message - `pleroma:report` - Someone was reported - `status` - Someone you are subscribed to created a status + - `update` - A status you boosted has been edited + - `admin.sign_up` - Someone signed up (optionally sent to admins) + - `admin.report` - A new report has been filed """ } end -- cgit v1.2.3 From b52d772a6e66b13bac875b7289de3bd63c087db3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 7 Jun 2024 16:03:10 -0400 Subject: Add some useful logging for ApiSpec errors --- lib/pleroma/web/api_spec/cast_and_validate.ex | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index f3e8e093e..eb487fce7 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -18,6 +18,8 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do alias OpenApiSpex.Plug.PutApiSpec alias Plug.Conn + require Logger + @impl Plug def init(opts) do opts @@ -51,6 +53,10 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do conn {:error, reason} -> + Logger.error( + "Strict ApiSpec: request denied to #{conn.path_info} with params #{inspect(conn.params)}" + ) + opts = render_error.init(reason) conn -- cgit v1.2.3 From 6ded017dee983e22890d639c1fbd12b44e04e964 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 4 Jun 2024 13:31:51 -0400 Subject: Do not start unused ConcurrentLimiter processes --- lib/pleroma/application.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 0d9757b44..d98293e8e 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -292,8 +292,6 @@ defmodule Pleroma.Application do config = Config.get(ConcurrentLimiter, []) [ - Pleroma.Web.RichMedia.Helpers, - Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, Pleroma.Search ] |> Enum.each(fn module -> -- cgit v1.2.3 From 5ed07aba7ad33dec57a16f6a6ecd17fa0538a3d2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 4 Jun 2024 13:28:00 -0400 Subject: Add missing pool to the type --- lib/pleroma/http/adapter_helper/gun.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/http/adapter_helper/gun.ex b/lib/pleroma/http/adapter_helper/gun.ex index 74ab9851e..1fe8dd4b2 100644 --- a/lib/pleroma/http/adapter_helper/gun.ex +++ b/lib/pleroma/http/adapter_helper/gun.ex @@ -15,7 +15,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do retry_timeout: 1_000 ] - @type pool() :: :federation | :upload | :media | :default + @type pool() :: :federation | :upload | :media | :rich_media | :default @spec options(keyword(), URI.t()) :: keyword() def options(incoming_opts \\ [], %URI{} = uri) do -- cgit v1.2.3 From b1c52c30628212f881825a702ba75e6170f82407 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 12:40:32 -0400 Subject: Rename Notification.send/1 to Notification.stream/1 Also update other places where we use the term "send" instead of "stream". This should make it clearer that we are streaming these over websockets / web push and not sending an activity. --- lib/pleroma/notification.ex | 5 +++-- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- lib/pleroma/web/activity_pub/side_effects.ex | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index f521a2998..de2508b93 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -757,8 +757,9 @@ defmodule Pleroma.Notification do |> Repo.update_all(set: [seen: true]) end - @spec send(list(Notification.t())) :: :ok - def send(notifications) do + @doc "Streams a list of notifications over websockets and web push" + @spec stream(list(Notification.t())) :: :ok + def stream(notifications) do Enum.each(notifications, fn notification -> Streamer.stream(["user", "user:notification"], notification) Push.send(notification) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1247ae7ce..6c1d6ded9 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -201,7 +201,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def notify_and_stream(activity) do {:ok, notifications} = Notification.create_notifications(activity) - Notification.send(notifications) + Notification.stream(notifications) original_activity = case activity do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 60b4d5f1b..2a141b0f5 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -592,9 +592,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do with {:ok, _} <- Repo.delete(object), do: :ok end - defp send_notifications(meta) do + defp stream_notifications(meta) do Keyword.get(meta, :notifications, []) - |> Notification.send() + |> Notification.stream() meta end @@ -625,7 +625,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do @impl true def handle_after_transaction(meta) do meta - |> send_notifications() + |> stream_notifications() |> send_streamables() end end -- cgit v1.2.3 From 471412ad34e87106641101c9e9e44fb1df2ca905 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 4 Jun 2024 14:39:03 -0400 Subject: Stream end of poll notification over websockets and web push --- lib/pleroma/notification.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index de2508b93..b9694a353 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -479,6 +479,8 @@ defmodule Pleroma.Notification do end end) + stream(notifications) + {:ok, notifications} end end -- cgit v1.2.3 From 07cb89823f2a33bc540fe53bd784e4b8e9197506 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 18:59:51 -0400 Subject: More robust validation the vapid config is set --- lib/pleroma/web/push.ex | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex index 0d43f402e..af895467b 100644 --- a/lib/pleroma/web/push.ex +++ b/lib/pleroma/web/push.ex @@ -20,16 +20,10 @@ defmodule Pleroma.Web.Push do end def vapid_config do - Application.get_env(:web_push_encryption, :vapid_details, []) + Application.get_env(:web_push_encryption, :vapid_details, nil) end - def enabled do - case vapid_config() do - [] -> false - list when is_list(list) -> true - _ -> false - end - end + def enabled, do: match?([subject: _, public_key: _, private_key: _], vapid_config()) def send(notification) do WebPusherWorker.enqueue("web_push", %{"notification_id" => notification.id}) -- cgit v1.2.3 From db88bf30d50a546114d3ee22a3c67b53975257d4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 19:20:38 -0400 Subject: Add spec for send/1 --- lib/pleroma/web/push.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex index af895467b..d4693f63e 100644 --- a/lib/pleroma/web/push.ex +++ b/lib/pleroma/web/push.ex @@ -25,6 +25,8 @@ defmodule Pleroma.Web.Push do def enabled, do: match?([subject: _, public_key: _, private_key: _], vapid_config()) + @spec send(Pleroma.Notification.t()) :: + {:ok, Oban.Job.t()} | {:error, Oban.Job.changeset() | term()} def send(notification) do WebPusherWorker.enqueue("web_push", %{"notification_id" => notification.id}) end -- cgit v1.2.3 From 86fa0889bc9906aced78b60ada932b569743340a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 19:30:27 -0400 Subject: Remove unnecessary mastodon_type hack --- lib/pleroma/web/push/impl.ex | 53 +++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 53334e72c..1853fdc2d 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -29,7 +29,6 @@ defmodule Pleroma.Web.Push.Impl do when activity_type in @types do actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) - mastodon_type = notification.type gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) avatar_url = User.avatar_url(actor) object = Object.normalize(activity, fetch: false) @@ -37,11 +36,11 @@ defmodule Pleroma.Web.Push.Impl do direct_conversation_id = Activity.direct_conversation_id(activity, user) for subscription <- fetch_subscriptions(user_id), - Subscription.enabled?(subscription, mastodon_type) do + Subscription.enabled?(subscription, notification.type) do %{ access_token: subscription.token.token, notification_id: notification.id, - notification_type: mastodon_type, + notification_type: notification.type, icon: avatar_url, preferred_locale: "en", pleroma: %{ @@ -49,7 +48,7 @@ defmodule Pleroma.Web.Push.Impl do direct_conversation_id: direct_conversation_id } } - |> Map.merge(build_content(notification, actor, object, mastodon_type)) + |> Map.merge(build_content(notification, actor, object)) |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end @@ -106,31 +105,24 @@ defmodule Pleroma.Web.Push.Impl do } end - def build_content(notification, actor, object, mastodon_type \\ nil) - def build_content( %{ user: %{notification_settings: %{hide_notification_contents: true}} } = notification, _actor, - _object, - mastodon_type + _object ) do - %{body: format_title(notification, mastodon_type)} + %{body: format_title(notification)} end - def build_content(notification, actor, object, mastodon_type) do - mastodon_type = mastodon_type || notification.type - + def build_content(notification, actor, object) do %{ - title: format_title(notification, mastodon_type), - body: format_body(notification, actor, object, mastodon_type) + title: format_title(notification), + body: format_body(notification, actor, object) } end - def format_body(activity, actor, object, mastodon_type \\ nil) - - def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}, _) do + def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}) do case data["content"] do nil -> "@#{actor.nickname}: (Attachment)" content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" @@ -140,8 +132,7 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => "Create"}}}, actor, - %{data: %{"content" => content}}, - _mastodon_type + %{data: %{"content" => content}} ) do "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end @@ -149,8 +140,7 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => "Announce"}}}, actor, - %{data: %{"content" => content}}, - _mastodon_type + %{data: %{"content" => content}} ) do "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end @@ -158,8 +148,7 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => "EmojiReact", "content" => content}}}, actor, - _object, - _mastodon_type + _object ) do "@#{actor.nickname} reacted with #{content}" end @@ -167,13 +156,10 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => type}}} = notification, actor, - _object, - mastodon_type + _object ) when type in ["Follow", "Like"] do - mastodon_type = mastodon_type || notification.type - - case mastodon_type do + case notification.type do "follow" -> "@#{actor.nickname} has followed you" "follow_request" -> "@#{actor.nickname} has requested to follow you" "favourite" -> "@#{actor.nickname} has favorited your post" @@ -183,20 +169,17 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => "Update"}}}, actor, - _object, - _mastodon_type + _object ) do "@#{actor.nickname} edited a status" end - def format_title(activity, mastodon_type \\ nil) - - def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do + def format_title(%{activity: %{data: %{"directMessage" => true}}}) do "New Direct Message" end - def format_title(%{type: type}, mastodon_type) do - case mastodon_type || type do + def format_title(%{type: type}) do + case type do "mention" -> "New Mention" "status" -> "New Status" "follow" -> "New Follower" -- cgit v1.2.3 From b1ef6e5e9ad5afc9d556a4d88ff34c9b62f5fea2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 19:48:41 -0400 Subject: Cleanup to make the code easier to follow --- lib/pleroma/web/push/impl.ex | 46 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 1853fdc2d..df7e0c53f 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -27,10 +27,10 @@ defmodule Pleroma.Web.Push.Impl do } = notification ) when activity_type in @types do - actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) + user = User.get_cached_by_ap_id(notification.activity.data["actor"]) gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) - avatar_url = User.avatar_url(actor) + avatar_url = User.avatar_url(user) object = Object.normalize(activity, fetch: false) user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) @@ -48,7 +48,7 @@ defmodule Pleroma.Web.Push.Impl do direct_conversation_id: direct_conversation_id } } - |> Map.merge(build_content(notification, actor, object)) + |> Map.merge(build_content(notification, user, object)) |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end @@ -109,71 +109,73 @@ defmodule Pleroma.Web.Push.Impl do %{ user: %{notification_settings: %{hide_notification_contents: true}} } = notification, - _actor, + _user, _object ) do %{body: format_title(notification)} end - def build_content(notification, actor, object) do + def build_content(notification, user, object) do %{ title: format_title(notification), - body: format_body(notification, actor, object) + body: format_body(notification, user, object) } end - def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}) do - case data["content"] do - nil -> "@#{actor.nickname}: (Attachment)" - content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" + @spec format_body(Notification.t(), User.t(), Object.t()) :: String.t() + def format_body(_notification, user, %{data: %{"type" => "ChatMessage"} = object}) do + case object["content"] do + nil -> "@#{user.nickname}: (Attachment)" + content -> "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end end def format_body( %{activity: %{data: %{"type" => "Create"}}}, - actor, + user, %{data: %{"content" => content}} ) do - "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" + "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( %{activity: %{data: %{"type" => "Announce"}}}, - actor, + user, %{data: %{"content" => content}} ) do - "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" + "@#{user.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( %{activity: %{data: %{"type" => "EmojiReact", "content" => content}}}, - actor, + user, _object ) do - "@#{actor.nickname} reacted with #{content}" + "@#{user.nickname} reacted with #{content}" end def format_body( %{activity: %{data: %{"type" => type}}} = notification, - actor, + user, _object ) when type in ["Follow", "Like"] do case notification.type do - "follow" -> "@#{actor.nickname} has followed you" - "follow_request" -> "@#{actor.nickname} has requested to follow you" - "favourite" -> "@#{actor.nickname} has favorited your post" + "follow" -> "@#{user.nickname} has followed you" + "follow_request" -> "@#{user.nickname} has requested to follow you" + "favourite" -> "@#{user.nickname} has favorited your post" end end def format_body( %{activity: %{data: %{"type" => "Update"}}}, - actor, + user, _object ) do - "@#{actor.nickname} edited a status" + "@#{user.nickname} edited a status" end + @spec format_title(Notification.t()) :: String.t() def format_title(%{activity: %{data: %{"directMessage" => true}}}) do "New Direct Message" end -- cgit v1.2.3 From 3211557f742e07d5144426a71c39267480656a38 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 20:30:43 -0400 Subject: Render nice web push notifications for polls --- lib/pleroma/web/push/impl.ex | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index df7e0c53f..98e7659ab 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -130,6 +130,24 @@ defmodule Pleroma.Web.Push.Impl do end end + def format_body( + %{type: "poll"} = _notification, + _user, + %{data: %{"content" => content} = data} = _object + ) do + options = Map.get(data, "anyOf") || Map.get(data, "oneOf") + + content_text = content <> "\n" + + options_text = + Enum.map(options, fn x -> "○ #{x["name"]}" end) + |> Enum.join("\n") + + [content_text, options_text] + |> Enum.join("\n") + |> Utils.scrub_html_and_truncate(80) + end + def format_body( %{activity: %{data: %{"type" => "Create"}}}, user, @@ -191,6 +209,7 @@ defmodule Pleroma.Web.Push.Impl do "update" -> "New Update" "pleroma:chat_mention" -> "New Chat Message" "pleroma:emoji_reaction" -> "New Reaction" + "poll" -> "Poll Results" type -> "New #{String.capitalize(type || "event")}" end end -- cgit v1.2.3 From dcc50da4009f19303dec36e0969f3fca0f690e4f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 22:40:08 -0400 Subject: Stream the notifications as part of the job --- lib/pleroma/notification.ex | 2 -- lib/pleroma/workers/poll_worker.ex | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index b9694a353..de2508b93 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -479,8 +479,6 @@ defmodule Pleroma.Notification do end end) - stream(notifications) - {:ok, notifications} end end diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 70df54193..3fcac9bc3 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -14,8 +14,9 @@ defmodule Pleroma.Workers.PollWorker do @impl Oban.Worker def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do - with %Activity{} = activity <- find_poll_activity(activity_id) do - Notification.create_poll_notifications(activity) + with %Activity{} = activity <- find_poll_activity(activity_id), + {:ok, notifications} <- Notification.create_poll_notifications(activity) do + Notification.stream(notifications) end end -- cgit v1.2.3 From c1b84edefcd8db998a7eabff110f2dabecfc8dde Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 8 Jun 2024 22:48:38 -0400 Subject: Increase web push character limit for the body --- lib/pleroma/web/push/impl.ex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 98e7659ab..13c054e05 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,6 +16,7 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query + @body_chars 140 @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"] @doc "Performs sending notifications for user subscriptions" @@ -126,7 +127,7 @@ defmodule Pleroma.Web.Push.Impl do def format_body(_notification, user, %{data: %{"type" => "ChatMessage"} = object}) do case object["content"] do nil -> "@#{user.nickname}: (Attachment)" - content -> "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" + content -> "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, @body_chars)}" end end @@ -145,7 +146,7 @@ defmodule Pleroma.Web.Push.Impl do [content_text, options_text] |> Enum.join("\n") - |> Utils.scrub_html_and_truncate(80) + |> Utils.scrub_html_and_truncate(@body_chars) end def format_body( @@ -153,7 +154,7 @@ defmodule Pleroma.Web.Push.Impl do user, %{data: %{"content" => content}} ) do - "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" + "@#{user.nickname}: #{Utils.scrub_html_and_truncate(content, @body_chars)}" end def format_body( @@ -161,7 +162,7 @@ defmodule Pleroma.Web.Push.Impl do user, %{data: %{"content" => content}} ) do - "@#{user.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" + "@#{user.nickname} repeated: #{Utils.scrub_html_and_truncate(content, @body_chars)}" end def format_body( -- cgit v1.2.3 From 5313255b1afcff394fbfe145081ac0ceb2cbe283 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 11 Jun 2024 16:06:18 -0400 Subject: Use conn.request_path for more legible error log --- lib/pleroma/web/api_spec/cast_and_validate.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index eb487fce7..672d1c4a1 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -54,7 +54,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do {:error, reason} -> Logger.error( - "Strict ApiSpec: request denied to #{conn.path_info} with params #{inspect(conn.params)}" + "Strict ApiSpec: request denied to #{conn.request_path} with params #{inspect(conn.params)}" ) opts = render_error.init(reason) -- cgit v1.2.3 From 568819c08afee68636a4871e78838db1ac1f590c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 11 Jun 2024 17:58:02 -0400 Subject: WebPush refactoring: separate build and deliver steps --- lib/pleroma/web/push/impl.ex | 95 ++++++++++++++++---------------- lib/pleroma/workers/web_pusher_worker.ex | 4 +- 2 files changed, 51 insertions(+), 48 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 13c054e05..c5ba7ca65 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -19,69 +19,72 @@ defmodule Pleroma.Web.Push.Impl do @body_chars 140 @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"] - @doc "Performs sending notifications for user subscriptions" - @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} - def perform( + @doc "Builds webpush notification payloads for the subscriptions enabled by the receiving user" + @spec build(Notification.t()) :: + list(%{content: map(), subscription: Subscription.t()}) + | :error + | {:error, :unknown_type} + def build( %{ activity: %{data: %{"type" => activity_type}} = activity, - user: %User{id: user_id} + user: user } = notification ) when activity_type in @types do - user = User.get_cached_by_ap_id(notification.activity.data["actor"]) + notification_actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) + avatar_url = User.avatar_url(notification_actor) - gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) - avatar_url = User.avatar_url(user) object = Object.normalize(activity, fetch: false) - user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) - for subscription <- fetch_subscriptions(user_id), - Subscription.enabled?(subscription, notification.type) do - %{ - access_token: subscription.token.token, - notification_id: notification.id, - notification_type: notification.type, - icon: avatar_url, - preferred_locale: "en", - pleroma: %{ - activity_id: notification.activity.id, - direct_conversation_id: direct_conversation_id + subscriptions = fetch_subscriptions(user.id) + + subscriptions + |> Enum.filter(&Subscription.enabled?(&1, notification.type)) + |> Enum.map(fn subscription -> + payload = + %{ + access_token: subscription.token.token, + notification_id: notification.id, + notification_type: notification.type, + icon: avatar_url, + preferred_locale: "en", + pleroma: %{ + activity_id: notification.activity.id, + direct_conversation_id: direct_conversation_id + } } - } - |> Map.merge(build_content(notification, user, object)) - |> Jason.encode!() - |> push_message(build_sub(subscription), gcm_api_key, subscription) - end - |> (&{:ok, &1}).() + |> Map.merge(build_content(notification, notification_actor, object)) + |> Jason.encode!() + + %{payload: payload, subscription: subscription} + end) end - def perform(_) do + def build(_) do Logger.warning("Unknown notification type") {:error, :unknown_type} end - @doc "Push message to web" - def push_message(body, sub, api_key, subscription) do - try do - case WebPushEncryption.send_web_push(body, sub, api_key) do - {:ok, %{status: code}} when code in 400..499 -> - Logger.debug("Removing subscription record") - Repo.delete!(subscription) - :ok + @doc "Deliver push notification to the provided webpush subscription" + @spec deliver(%{payload: String.t(), subscription: Subscription.t()}) :: :ok | :error + def deliver(%{payload: payload, subscription: subscription}) do + gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) + formatted_subscription = build_sub(subscription) + + case WebPushEncryption.send_web_push(payload, formatted_subscription, gcm_api_key) do + {:ok, %{status: code}} when code in 200..299 -> + :ok - {:ok, %{status: code}} when code in 200..299 -> - :ok + {:ok, %{status: code}} when code in 400..499 -> + Logger.debug("Removing subscription record") + Repo.delete!(subscription) + :ok - {:ok, %{status: code}} -> - Logger.error("Web Push Notification failed with code: #{code}") - :error + {:ok, %{status: code}} -> + Logger.error("Web Push Notification failed with code: #{code}") + :error - error -> - Logger.error("Web Push Notification failed with #{inspect(error)}") - :error - end - rescue error -> Logger.error("Web Push Notification failed with #{inspect(error)}") :error @@ -140,9 +143,7 @@ defmodule Pleroma.Web.Push.Impl do content_text = content <> "\n" - options_text = - Enum.map(options, fn x -> "○ #{x["name"]}" end) - |> Enum.join("\n") + options_text = Enum.map_join(options, "\n", fn x -> "○ #{x["name"]}" end) [content_text, options_text] |> Enum.join("\n") diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex index 67e84b0c9..c549d3cd6 100644 --- a/lib/pleroma/workers/web_pusher_worker.ex +++ b/lib/pleroma/workers/web_pusher_worker.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Workers.WebPusherWorker do alias Pleroma.Notification alias Pleroma.Repo + alias Pleroma.Web.Push.Impl use Pleroma.Workers.WorkerHelper, queue: "web_push" @@ -15,7 +16,8 @@ defmodule Pleroma.Workers.WebPusherWorker do |> Repo.get(notification_id) |> Repo.preload([:activity, :user]) - Pleroma.Web.Push.Impl.perform(notification) + Impl.build(notification) + |> Enum.each(&Impl.deliver(&1)) end @impl Oban.Worker -- cgit v1.2.3 From 603a57576638c9732fe873db3c2b8d56d0a413a5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 11 Jun 2024 18:14:07 -0400 Subject: The user is not always preloaded into the notification --- lib/pleroma/web/push/impl.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index c5ba7ca65..65801922d 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -27,7 +27,7 @@ defmodule Pleroma.Web.Push.Impl do def build( %{ activity: %{data: %{"type" => activity_type}} = activity, - user: user + user_id: user_id } = notification ) when activity_type in @types do @@ -35,9 +35,10 @@ defmodule Pleroma.Web.Push.Impl do avatar_url = User.avatar_url(notification_actor) object = Object.normalize(activity, fetch: false) + user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) - subscriptions = fetch_subscriptions(user.id) + subscriptions = fetch_subscriptions(user_id) subscriptions |> Enum.filter(&Subscription.enabled?(&1, notification.type)) -- cgit v1.2.3 From 6a9d9da26feefe9b19e8072fd378f01a977b9d1a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 11 Jun 2024 18:38:41 -0400 Subject: Cyclical complexity --- lib/pleroma/web/push/impl.ex | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 65801922d..2bcfa97f5 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -201,19 +201,15 @@ defmodule Pleroma.Web.Push.Impl do "New Direct Message" end - def format_title(%{type: type}) do - case type do - "mention" -> "New Mention" - "status" -> "New Status" - "follow" -> "New Follower" - "follow_request" -> "New Follow Request" - "reblog" -> "New Repeat" - "favourite" -> "New Favorite" - "update" -> "New Update" - "pleroma:chat_mention" -> "New Chat Message" - "pleroma:emoji_reaction" -> "New Reaction" - "poll" -> "Poll Results" - type -> "New #{String.capitalize(type || "event")}" - end - end + def format_title(%{type: "mention"}), do: "New Mention" + def format_title(%{type: "status"}), do: "New Status" + def format_title(%{type: "follow"}), do: "New Follower" + def format_title(%{type: "follow_request"}), do: "New Follow Request" + def format_title(%{type: "reblog"}), do: "New Repeat" + def format_title(%{type: "favourite"}), do: "New Favorite" + def format_title(%{type: "update"}), do: "New Update" + def format_title(%{type: "pleroma:chat_mention"}), do: "New Chat Message" + def format_title(%{type: "pleroma:emoji_reaction"}), do: "New Reaction" + def format_title(%{type: "poll"}), do: "Poll Results" + def format_title(%{type: type}), do: "New #{String.capitalize(type || "event")}" end -- cgit v1.2.3 From 5c8afbe646c874eea32d7063aa499c97191f3a6e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 11 Jun 2024 18:54:22 -0400 Subject: Fix tests --- lib/pleroma/web/push/impl.ex | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 2bcfa97f5..d71e134cb 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -21,9 +21,7 @@ defmodule Pleroma.Web.Push.Impl do @doc "Builds webpush notification payloads for the subscriptions enabled by the receiving user" @spec build(Notification.t()) :: - list(%{content: map(), subscription: Subscription.t()}) - | :error - | {:error, :unknown_type} + list(%{content: map(), subscription: Subscription.t()}) | [] def build( %{ activity: %{data: %{"type" => activity_type}} = activity, @@ -62,9 +60,9 @@ defmodule Pleroma.Web.Push.Impl do end) end - def build(_) do - Logger.warning("Unknown notification type") - {:error, :unknown_type} + def build(notif) do + Logger.warning("WebPush: unknown activity type: #{inspect(notif)}") + [] end @doc "Deliver push notification to the provided webpush subscription" -- cgit v1.2.3 From 1ae5c2b020810eda7243e7e6b52cf89e6bb7f8d0 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 12 Jun 2024 12:40:01 +0400 Subject: Transmogrifier: Encode Emoji id to be valid. --- lib/pleroma/web/activity_pub/transmogrifier.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index edfe73a25..4d851559f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -913,9 +913,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def add_emoji_tags(object), do: object - defp build_emoji_tag({name, url}) do + def build_emoji_tag({name, url}) do + url = URI.encode(url) + %{ - "icon" => %{"url" => "#{URI.encode(url)}", "type" => "Image"}, + "icon" => %{"url" => "#{url}", "type" => "Image"}, "name" => ":" <> name <> ":", "type" => "Emoji", "updated" => "1970-01-01T00:00:00Z", -- cgit v1.2.3 From cb91dab75f70ade96028c32e270a27352c41714e Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 31 May 2024 07:19:48 +0200 Subject: Switch formatting checks to Elixir 1.15 --- lib/mix/tasks/pleroma/database.ex | 2 +- lib/pleroma/emoji/pack.ex | 6 +++--- lib/pleroma/object/updater.ex | 5 ++++- lib/pleroma/user/backup.ex | 12 ++++++------ lib/pleroma/web/api_spec/operations/search_operation.ex | 4 +++- lib/pleroma/web/auth/ldap_authenticator.ex | 2 +- lib/pleroma/web/mastodon_api/websocket_handler.ex | 2 +- lib/pleroma/web/plugs/o_auth_scopes_plug.ex | 4 +++- lib/pleroma/web/xml.ex | 2 +- 9 files changed, 23 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 13ac6536c..b82d1f079 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -351,7 +351,7 @@ defmodule Mix.Tasks.Pleroma.Database do ) end - shell_info('Done.') + shell_info(~c"Done.") end end diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index afc341853..785fdb8b2 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -416,10 +416,10 @@ defmodule Pleroma.Emoji.Pack do end defp create_archive_and_cache(pack, hash) do - files = ['pack.json' | Enum.map(pack.files, fn {_, file} -> to_charlist(file) end)] + files = [~c"pack.json" | Enum.map(pack.files, fn {_, file} -> to_charlist(file) end)] {:ok, {_, result}} = - :zip.zip('#{pack.name}.zip', files, [:memory, cwd: to_charlist(pack.path)]) + :zip.zip(~c"#{pack.name}.zip", files, [:memory, cwd: to_charlist(pack.path)]) ttl_per_file = Pleroma.Config.get!([:emoji, :shared_pack_cache_seconds_per_file]) overall_ttl = :timer.seconds(ttl_per_file * Enum.count(files)) @@ -586,7 +586,7 @@ defmodule Pleroma.Emoji.Pack do with :ok <- File.mkdir_p!(local_pack.path) do files = Enum.map(remote_pack["files"], fn {_, path} -> to_charlist(path) end) # Fallback cannot contain a pack.json file - files = if pack_info[:fallback], do: files, else: ['pack.json' | files] + files = if pack_info[:fallback], do: files, else: [~c"pack.json" | files] :zip.unzip(archive, cwd: to_charlist(local_pack.path), file_list: files) end diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex index b1e4870ba..b80bc7faf 100644 --- a/lib/pleroma/object/updater.ex +++ b/lib/pleroma/object/updater.ex @@ -134,7 +134,10 @@ defmodule Pleroma.Object.Updater do else %{updated_object: updated_data} = updated_data - |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false) + |> maybe_update_history(original_data, + updated: updated, + use_history_in_new_object?: false + ) updated_data |> Map.put("updated", date) diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index 65e0baccd..1821de667 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -197,12 +197,12 @@ defmodule Pleroma.User.Backup do end @files [ - 'actor.json', - 'outbox.json', - 'likes.json', - 'bookmarks.json', - 'followers.json', - 'following.json' + ~c"actor.json", + ~c"outbox.json", + ~c"likes.json", + ~c"bookmarks.json", + ~c"followers.json", + ~c"following.json" ] @spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error def export(%__MODULE__{} = backup, caller_pid) do diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex index 539743ba3..c2afe8e18 100644 --- a/lib/pleroma/web/api_spec/operations/search_operation.ex +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -79,7 +79,9 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do %Schema{type: :string, enum: ["accounts", "hashtags", "statuses"]}, "Search type" ), - Operation.parameter(:q, :query, %Schema{type: :string}, "The search query", required: true), + Operation.parameter(:q, :query, %Schema{type: :string}, "The search query", + required: true + ), Operation.parameter( :resolve, :query, diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index e8cd4491c..17ffd820d 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -110,7 +110,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do } params = - case List.keyfind(attributes, 'mail', 0) do + case List.keyfind(attributes, ~c"mail", 0) do {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) _ -> params end diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index bb27d806d..730295a4c 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -104,7 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end def handle_info(:close, state) do - {:stop, {:closed, 'connection closed by server'}, state} + {:stop, {:closed, ~c"connection closed by server"}, state} end def handle_info(msg, state) do diff --git a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex index faf0fd8c6..08c2f61ea 100644 --- a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex @@ -34,7 +34,9 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlug do permissions = Enum.join(missing_scopes, " #{op} ") error_message = - dgettext("errors", "Insufficient permissions: %{permissions}.", permissions: permissions) + dgettext("errors", "Insufficient permissions: %{permissions}.", + permissions: permissions + ) conn |> put_resp_content_type("application/json") diff --git a/lib/pleroma/web/xml.ex b/lib/pleroma/web/xml.ex index 64329e4ba..3997e1661 100644 --- a/lib/pleroma/web/xml.ex +++ b/lib/pleroma/web/xml.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.XML do def string_from_xpath(xpath, doc) do try do - {:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc) + {:xmlObj, :string, res} = :xmerl_xpath.string(~c"string(#{xpath})", doc) res = res -- cgit v1.2.3 From 66ac2e9b81a46b38c9ab9ba793c36987d3171911 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 2 Aug 2023 11:00:18 +0200 Subject: Upload.base_url: Don't pass nil to Path.join(), don't return nil --- lib/pleroma/upload.ex | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 35c7c02a5..b0aef2592 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -249,14 +249,16 @@ defmodule Pleroma.Upload do defp url_from_spec(_upload, _base_url, {:url, url}), do: url + @spec base_url() :: binary def base_url do uploader = @config_impl.get([Pleroma.Upload, :uploader]) - upload_base_url = @config_impl.get([Pleroma.Upload, :base_url]) + upload_fallback_url = Pleroma.Web.Endpoint.url() <> "/media/" + upload_base_url = @config_impl.get([Pleroma.Upload, :base_url]) || upload_fallback_url public_endpoint = @config_impl.get([uploader, :public_endpoint]) case uploader do Pleroma.Uploaders.Local -> - upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/" + upload_base_url Pleroma.Uploaders.S3 -> bucket = @config_impl.get([Pleroma.Uploaders.S3, :bucket]) @@ -268,11 +270,14 @@ defmodule Pleroma.Upload do !is_nil(truncated_namespace) -> truncated_namespace - !is_nil(namespace) -> + !is_nil(namespace) and !is_nil(bucket) -> namespace <> ":" <> bucket - true -> + !is_nil(bucket) -> bucket + + true -> + "" end if public_endpoint do @@ -285,7 +290,7 @@ defmodule Pleroma.Upload do @config_impl.get([Pleroma.Uploaders.IPFS, :get_gateway_url]) _ -> - public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/" + public_endpoint || upload_base_url end end end -- cgit v1.2.3 From 2180537a2b92362c75e7d082fea026062fbd4b9b Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 2 Aug 2023 12:30:09 +0200 Subject: MediaProxy: :whitelist config fallback to [] --- lib/pleroma/web/media_proxy.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/media_proxy.ex b/lib/pleroma/web/media_proxy.ex index d64760fc2..29882542c 100644 --- a/lib/pleroma/web/media_proxy.ex +++ b/lib/pleroma/web/media_proxy.ex @@ -75,8 +75,7 @@ defmodule Pleroma.Web.MediaProxy do %{host: domain} = URI.parse(url) mediaproxy_whitelist_domains = - [:media_proxy, :whitelist] - |> Config.get() + Config.get([:media_proxy, :whitelist], []) |> Kernel.++(["#{Upload.base_url()}"]) |> Enum.map(&maybe_get_domain_from_url/1) -- cgit v1.2.3 From c389ea0f42bf01bbc463e9e237162aab3dabf7a7 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sun, 24 Dec 2023 10:45:29 +0100 Subject: Fix compatibility with Loggers in Elixir 1.15+ --- lib/pleroma/config/transfer_task.ex | 31 +++++++++++++++++++++---------- lib/pleroma/config_db.ex | 9 +++++++-- 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 91885347f..1210e2c9d 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors +# Copyright © 2017-2023 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.TransferTask do @@ -13,6 +13,13 @@ defmodule Pleroma.Config.TransferTask do @type env() :: :test | :benchmark | :dev | :prod + @add_backend if Version.match?(System.version(), "< 1.15.0-rc.0"), + do: &Logger.add_backend/1, + else: &LoggerBackends.add/1 + @remove_backend if Version.match?(System.version(), "< 1.15.0-rc.0"), + do: &Logger.remove_backend/1, + else: &LoggerBackends.remove/1 + defp reboot_time_keys, do: [ {:pleroma, :hackney_pools}, @@ -105,26 +112,30 @@ defmodule Pleroma.Config.TransferTask do # change logger configuration in runtime, without restart defp configure({_, :backends, _, merged}) do # removing current backends - Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1) + Enum.each(Application.get_env(:logger, :backends), @remove_backend) - Enum.each(merged, &Logger.add_backend/1) + Enum.each(merged, @add_backend) :ok = update_env(:logger, :backends, merged) end - defp configure({_, key, _, merged}) when key in [:console, :ex_syslogger] do + defp configure({_, key, _, merged}) + when key in [:console, Logger.Backends.Console, :ex_syslogger] do + backend = + case key do + :ex_syslogger -> {ExSyslogger, :ex_syslogger} + :console -> Logger.Backends.Console + Logger.Backends.Console -> Logger.Backends.Console + key -> key + end + merged = - if key == :console do + if backend == Logger.Backends.Console do put_in(merged[:format], merged[:format] <> "\n") else merged end - backend = - if key == :ex_syslogger, - do: {ExSyslogger, :ex_syslogger}, - else: key - Logger.configure_backend(backend, merged) :ok = update_env(:logger, key, merged) end diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index e28fcb124..b605d2843 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -385,7 +385,12 @@ defmodule Pleroma.ConfigDB do @spec module_name?(String.t()) :: boolean() def module_name?(string) do - Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Ueberauth|Swoosh)\./, string) or - string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"] + if String.contains?(string, ".") do + [name | _] = String.split(string, ".", parts: 2) + + name in ~w[Pleroma Phoenix Tesla Ueberauth Swoosh Logger LoggerBackends] + else + string in ~w[Oban Ueberauth ExSyslogger ConcurrentLimiter] + end end end -- cgit v1.2.3 From 41434ffcec47510de86cd9b06eb32521b897a1c9 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 6 Jun 2024 19:14:38 +0400 Subject: Tests: Don't spawn processes in tests. --- lib/pleroma/web/mastodon_api/views/status_view.ex | 9 ++++++++- lib/pleroma/web/streamer.ex | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0c16749a4..963658b1e 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do require Pleroma.Constants alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.HTML alias Pleroma.Maps alias Pleroma.Object @@ -30,7 +31,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do # pagination is restricted to 40 activities at a time defp fetch_rich_media_for_activities(activities) do Enum.each(activities, fn activity -> - spawn(fn -> Card.get_by_activity(activity) end) + fun = fn -> Card.get_by_activity(activity) end + + if Config.get([__MODULE__, :sync_fetching], false) do + fun.() + else + spawn(fun) + end end) end diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 7065fdab2..9abdfae30 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -172,7 +172,13 @@ defmodule Pleroma.Web.Streamer do def stream(topics, items) do if should_env_send?() do for topic <- List.wrap(topics), item <- List.wrap(items) do - spawn(fn -> do_stream(topic, item) end) + fun = fn -> do_stream(topic, item) end + + if Config.get([__MODULE__, :sync_streaming], false) do + fun.() + else + spawn(fun) + end end end end -- cgit v1.2.3 From cbf8f8ac0d65e7606e5b0bdd046c9106107c68b2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 13 Jun 2024 10:48:50 -0400 Subject: Fix mix pleroma.config dump on Elixir 1.15 --- lib/mix/pleroma.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 2976085ba..c01cf054d 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -14,7 +14,8 @@ defmodule Mix.Pleroma do :swoosh, :timex, :fast_html, - :oban + :oban, + :logger_backends ] @cachex_children ["object", "user", "scrubber", "web_resp"] @doc "Common functions to be reused in mix tasks" -- cgit v1.2.3 From e37845cd351d0d9cbdae469b75a532edbaa3c0ed Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 16 Jun 2024 16:26:24 -0400 Subject: Stale user refreshing should be done async to prevent blocking of rendering activities --- lib/pleroma/user.ex | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 884c1f302..440dc9210 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2154,20 +2154,23 @@ defmodule Pleroma.User do def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id) + @spec get_or_fetch_by_ap_id(String.t()) :: {:ok, User.t()} | {:error, any()} def get_or_fetch_by_ap_id(ap_id) do - cached_user = get_cached_by_ap_id(ap_id) - - maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id) - - case {cached_user, maybe_fetched_user} do - {_, {:ok, %User{} = user}} -> - {:ok, user} + with cached_user = %User{} <- get_cached_by_ap_id(ap_id), + _ <- maybe_refresh(cached_user) do + {:ok, cached_user} + else + _ -> fetch_by_ap_id(ap_id) + end + end - {%User{} = user, _} -> - {:ok, user} + defp maybe_refresh(user) do + fun = fn -> needs_update?(user) && fetch_by_ap_id(user.ap_id) end - _ -> - {:error, :not_found} + if Config.get([__MODULE__, :sync_refreshing], false) do + fun.() + else + Task.start(fun) end end -- cgit v1.2.3 From 9c6763725547e4927d09cf3cd8d33949a28c4824 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 17 Jun 2024 10:08:54 -0400 Subject: Refactor the async user refreshing to use Oban Previous implementation could cause duplicate simultaneous profile fetches which is not polite. --- lib/pleroma/user.ex | 10 ++++------ lib/pleroma/workers/user_refresh_worker.ex | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/workers/user_refresh_worker.ex (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 440dc9210..7a8a68931 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -38,6 +38,7 @@ defmodule Pleroma.User do alias Pleroma.Web.OAuth alias Pleroma.Web.RelMe alias Pleroma.Workers.BackgroundWorker + alias Pleroma.Workers.UserRefreshWorker require Logger require Pleroma.Constants @@ -2165,12 +2166,9 @@ defmodule Pleroma.User do end defp maybe_refresh(user) do - fun = fn -> needs_update?(user) && fetch_by_ap_id(user.ap_id) end - - if Config.get([__MODULE__, :sync_refreshing], false) do - fun.() - else - Task.start(fun) + if needs_update?(user) do + UserRefreshWorker.new(%{"ap_id" => user.ap_id}) + |> Oban.insert() end end diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex new file mode 100644 index 000000000..5842143f8 --- /dev/null +++ b/lib/pleroma/workers/user_refresh_worker.ex @@ -0,0 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.UserRefreshWorker do + use Pleroma.Workers.WorkerHelper, queue: "background", max_attempts: 1, unique: [period: 300] + + alias Pleroma.User + + @impl Oban.Worker + def perform(%Job{args: %{"ap_id" => ap_id}}) do + User.fetch_by_ap_id(ap_id) + end +end -- cgit v1.2.3 From 3a8420b141c5186a02be702b59a3799ff65cf4f8 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 19 Jun 2024 14:29:44 -0400 Subject: Remove remaining vestiges of Logger support in ConfigDB/TransferTask --- lib/pleroma/config/transfer_task.ex | 52 ++----------------------------------- lib/pleroma/config_db.ex | 3 +-- 2 files changed, 3 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 1210e2c9d..ffc95f144 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -13,13 +13,6 @@ defmodule Pleroma.Config.TransferTask do @type env() :: :test | :benchmark | :dev | :prod - @add_backend if Version.match?(System.version(), "< 1.15.0-rc.0"), - do: &Logger.add_backend/1, - else: &LoggerBackends.add/1 - @remove_backend if Version.match?(System.version(), "< 1.15.0-rc.0"), - do: &Logger.remove_backend/1, - else: &LoggerBackends.remove/1 - defp reboot_time_keys, do: [ {:pleroma, :hackney_pools}, @@ -51,14 +44,9 @@ defmodule Pleroma.Config.TransferTask do with {_, true} <- {:configurable, Config.get(:configurable_from_database)} do # We need to restart applications for loaded settings take effect - {logger, other} = + settings = (Repo.all(ConfigDB) ++ deleted_settings) |> Enum.map(&merge_with_default/1) - |> Enum.split_with(fn {group, _, _, _} -> group in [:logger] end) - - logger - |> Enum.sort() - |> Enum.each(&configure/1) started_applications = Application.started_applications() @@ -71,7 +59,7 @@ defmodule Pleroma.Config.TransferTask do [:pleroma | reject] end - other + settings |> Enum.map(&update/1) |> Enum.uniq() |> Enum.reject(&(&1 in reject)) @@ -109,42 +97,6 @@ defmodule Pleroma.Config.TransferTask do {group, key, value, merged} end - # change logger configuration in runtime, without restart - defp configure({_, :backends, _, merged}) do - # removing current backends - Enum.each(Application.get_env(:logger, :backends), @remove_backend) - - Enum.each(merged, @add_backend) - - :ok = update_env(:logger, :backends, merged) - end - - defp configure({_, key, _, merged}) - when key in [:console, Logger.Backends.Console, :ex_syslogger] do - backend = - case key do - :ex_syslogger -> {ExSyslogger, :ex_syslogger} - :console -> Logger.Backends.Console - Logger.Backends.Console -> Logger.Backends.Console - key -> key - end - - merged = - if backend == Logger.Backends.Console do - put_in(merged[:format], merged[:format] <> "\n") - else - merged - end - - Logger.configure_backend(backend, merged) - :ok = update_env(:logger, key, merged) - end - - defp configure({_, key, _, merged}) do - Logger.configure([{key, merged}]) - :ok = update_env(:logger, key, merged) - end - defp update({group, key, value, merged}) do try do :ok = update_env(group, key, merged) diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index b605d2843..89d3050d6 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -165,8 +165,7 @@ defmodule Pleroma.ConfigDB do {:pleroma, :ecto_repos}, {:mime, :types}, {:cors_plug, [:max_age, :methods, :expose, :headers]}, - {:swarm, :node_blacklist}, - {:logger, :backends} + {:swarm, :node_blacklist} ] Enum.any?(full_key_update, fn -- cgit v1.2.3 From ed2976b237b53a524247d564691e0a12d3231d68 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 19 Jun 2024 19:04:09 -0400 Subject: Custom mix task to retry failed tests once in CI pipeline This will be temporary* as we hunt down the cause of the random test failures * gonna regret this --- lib/mix/tasks/pleroma/test_runner.ex | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/mix/tasks/pleroma/test_runner.ex (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/test_runner.ex b/lib/mix/tasks/pleroma/test_runner.ex new file mode 100644 index 000000000..69fefb001 --- /dev/null +++ b/lib/mix/tasks/pleroma/test_runner.ex @@ -0,0 +1,25 @@ +defmodule Mix.Tasks.Pleroma.TestRunner do + @shortdoc "Retries tests once if they fail" + + use Mix.Task + + def run(args \\ []) do + case System.cmd("mix", ["test"] ++ args, into: IO.stream(:stdio, :line)) do + {_, 0} -> + :ok + + _ -> + retry(args) + end + end + + def retry(args) do + case System.cmd("mix", ["test", "--failed"] ++ args, into: IO.stream(:stdio, :line)) do + {_, 0} -> + :ok + + _ -> + exit(1) + end + end +end -- cgit v1.2.3 From 4dfa50f256ce7966ff127a12f91c9fdeabfff114 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 19 Jun 2024 21:25:24 -0400 Subject: Rename RichMediaExpirationWorker to RichMediaWorker --- lib/pleroma/web/rich_media/backfill.ex | 4 ++-- lib/pleroma/workers/rich_media_expiration_worker.ex | 15 --------------- lib/pleroma/workers/rich_media_worker.ex | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 lib/pleroma/workers/rich_media_expiration_worker.ex create mode 100644 lib/pleroma/workers/rich_media_worker.ex (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index 4ec50e132..46b879434 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do alias Pleroma.Web.RichMedia.Card alias Pleroma.Web.RichMedia.Parser alias Pleroma.Web.RichMedia.Parser.TTL - alias Pleroma.Workers.RichMediaExpirationWorker + alias Pleroma.Workers.RichMediaWorker require Logger @@ -72,7 +72,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do {:ok, ttl} when is_number(ttl) -> timestamp = DateTime.from_unix!(ttl) - RichMediaExpirationWorker.new(%{"url" => url}, scheduled_at: timestamp) + RichMediaWorker.new(%{"op" => "expire", "url" => url}, scheduled_at: timestamp) |> Oban.insert() _ -> diff --git a/lib/pleroma/workers/rich_media_expiration_worker.ex b/lib/pleroma/workers/rich_media_expiration_worker.ex deleted file mode 100644 index 0b74687cf..000000000 --- a/lib/pleroma/workers/rich_media_expiration_worker.ex +++ /dev/null @@ -1,15 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.RichMediaExpirationWorker do - alias Pleroma.Web.RichMedia.Card - - use Oban.Worker, - queue: :background - - @impl Oban.Worker - def perform(%Job{args: %{"url" => url} = _args}) do - Card.delete(url) - end -end diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex new file mode 100644 index 000000000..968395c64 --- /dev/null +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.RichMediaWorker do + alias Pleroma.Web.RichMedia.Card + + use Oban.Worker, + queue: :background + + @impl Oban.Worker + def perform(%Job{args: %{"op" => "expire", "url" => url} = _args}) do + Card.delete(url) + end +end -- cgit v1.2.3 From 17d04ccc8bcf3f0e033ff4333c6153edf904d4f8 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 19 Jun 2024 23:18:38 -0400 Subject: RichMedia backfill processing through Oban --- lib/pleroma/web/mastodon_api/views/status_view.ex | 9 +---- lib/pleroma/web/rich_media/backfill.ex | 45 +++++------------------ lib/pleroma/web/rich_media/card.ex | 13 ++++--- lib/pleroma/workers/rich_media_worker.ex | 8 +++- 4 files changed, 23 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 963658b1e..d9d7e516a 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do require Pleroma.Constants alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.HTML alias Pleroma.Maps alias Pleroma.Object @@ -31,13 +30,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do # pagination is restricted to 40 activities at a time defp fetch_rich_media_for_activities(activities) do Enum.each(activities, fn activity -> - fun = fn -> Card.get_by_activity(activity) end - - if Config.get([__MODULE__, :sync_fetching], false) do - fun.() - else - spawn(fun) - end + Card.get_by_activity(activity) end) end diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index 46b879434..1d8cc87d4 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -10,31 +10,21 @@ defmodule Pleroma.Web.RichMedia.Backfill do require Logger - @backfiller Pleroma.Config.get([__MODULE__, :provider], Pleroma.Web.RichMedia.Backfill.Task) @cachex Pleroma.Config.get([:cachex, :provider], Cachex) - @max_attempts 3 - @retry 5_000 - def start(%{url: url} = args) when is_binary(url) do + @spec run(map()) :: + :ok | {:error, {:invalid_metadata, any()} | :body_too_large | {:content, any()} | any()} + def run(%{"url" => url} = args) do url_hash = Card.url_to_hash(url) - args = - args - |> Map.put(:attempt, 1) - |> Map.put(:url_hash, url_hash) - - @backfiller.run(args) - end - - def run(%{url: url, url_hash: url_hash, attempt: attempt} = args) - when attempt <= @max_attempts do case Parser.parse(url) do {:ok, fields} -> {:ok, card} = Card.create(url, fields) maybe_schedule_expiration(url, fields) - if Map.has_key?(args, :activity_id) do + with %{"activity_id" => activity_id} <- args, + false <- is_nil(activity_id) do stream_update(args) end @@ -54,19 +44,10 @@ defmodule Pleroma.Web.RichMedia.Backfill do e -> Logger.debug("Rich media error for #{url}: #{inspect(e)}") - - :timer.sleep(@retry * attempt) - - run(%{args | attempt: attempt + 1}) + {:error, e} end end - def run(%{url: url, url_hash: url_hash}) do - Logger.debug("Rich media failure for #{url}") - - negative_cache(url_hash, :timer.minutes(15)) - end - defp maybe_schedule_expiration(url, fields) do case TTL.process(fields, url) do {:ok, ttl} when is_number(ttl) -> @@ -80,22 +61,14 @@ defmodule Pleroma.Web.RichMedia.Backfill do end end - defp stream_update(%{activity_id: activity_id}) do + defp stream_update(%{"activity_id" => activity_id}) do Pleroma.Activity.get_by_id(activity_id) |> Pleroma.Activity.normalize() |> Pleroma.Web.ActivityPub.ActivityPub.stream_out() end defp warm_cache(key, val), do: @cachex.put(:rich_media_cache, key, val) - defp negative_cache(key, ttl \\ nil), do: @cachex.put(:rich_media_cache, key, nil, ttl: ttl) -end -defmodule Pleroma.Web.RichMedia.Backfill.Task do - alias Pleroma.Web.RichMedia.Backfill - - def run(args) do - Task.Supervisor.start_child(Pleroma.TaskSupervisor, Backfill, :run, [args], - name: {:global, {:rich_media, args.url_hash}} - ) - end + defp negative_cache(key, ttl \\ :timer.minutes(15)), + do: @cachex.put(:rich_media_cache, key, nil, ttl: ttl) end diff --git a/lib/pleroma/web/rich_media/card.ex b/lib/pleroma/web/rich_media/card.ex index 040066f36..72ff5e791 100644 --- a/lib/pleroma/web/rich_media/card.ex +++ b/lib/pleroma/web/rich_media/card.ex @@ -7,8 +7,8 @@ defmodule Pleroma.Web.RichMedia.Card do alias Pleroma.HTML alias Pleroma.Object alias Pleroma.Repo - alias Pleroma.Web.RichMedia.Backfill alias Pleroma.Web.RichMedia.Parser + alias Pleroma.Workers.RichMediaWorker @cachex Pleroma.Config.get([:cachex, :provider], Cachex) @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) @@ -75,17 +75,18 @@ defmodule Pleroma.Web.RichMedia.Card do def get_by_url(nil), do: nil - @spec get_or_backfill_by_url(String.t(), map()) :: t() | nil - def get_or_backfill_by_url(url, backfill_opts \\ %{}) do + @spec get_or_backfill_by_url(String.t(), keyword()) :: t() | nil + def get_or_backfill_by_url(url, opts \\ []) do if @config_impl.get([:rich_media, :enabled]) do case get_by_url(url) do %__MODULE__{} = card -> card nil -> - backfill_opts = Map.put(backfill_opts, :url, url) + activity_id = Keyword.get(opts, :activity, nil) - Backfill.start(backfill_opts) + RichMediaWorker.new(%{"op" => "backfill", "url" => url, "activity_id" => activity_id}) + |> Oban.insert() nil @@ -137,7 +138,7 @@ defmodule Pleroma.Web.RichMedia.Card do nil else {:cached, url} -> - get_or_backfill_by_url(url, %{activity_id: activity.id}) + get_or_backfill_by_url(url, activity_id: activity.id) _ -> :error diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index 968395c64..f18ac658a 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -3,13 +3,17 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.RichMediaWorker do + alias Pleroma.Web.RichMedia.Backfill alias Pleroma.Web.RichMedia.Card - use Oban.Worker, - queue: :background + use Oban.Worker, queue: :background, max_attempts: 3, unique: [period: 300] @impl Oban.Worker def perform(%Job{args: %{"op" => "expire", "url" => url} = _args}) do Card.delete(url) end + + def perform(%Job{args: %{"op" => "backfill", "url" => _url} = args}) do + Backfill.run(args) + end end -- cgit v1.2.3 From c765fcbe7e907dd5ac1f8b559bf65ab477dfe0f0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 20 Jun 2024 14:03:22 -0400 Subject: Gun Connection Pool: successfully retry after reclaiming the pool --- .../gun/connection_pool/worker_supervisor.ex | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index eb83962d8..dabb15b08 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -5,6 +5,9 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do @moduledoc "Supervisor for pool workers. Does not do anything except enforce max connection limit" + alias Pleroma.Config + alias Pleroma.Gun.ConnectionPool.Worker + use DynamicSupervisor def start_link(opts) do @@ -14,21 +17,28 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do def init(_opts) do DynamicSupervisor.init( strategy: :one_for_one, - max_children: Pleroma.Config.get([:connections_pool, :max_connections]) + max_children: Config.get([:connections_pool, :max_connections]) ) end - def start_worker(opts, last_attempt \\ false) do - case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do + def start_worker(opts, last_attempt \\ false) + + def start_worker(opts, true) do + case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do + {:error, :max_children} -> + :telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts}) + {:error, :pool_full} + + res -> + res + end + end + + def start_worker(opts, false) do + case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do {:error, :max_children} -> - funs = [fn -> last_attempt end, fn -> match?(:error, free_pool()) end] - - if Enum.any?(funs, fn fun -> fun.() end) do - :telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts}) - {:error, :pool_full} - else - start_worker(opts, true) - end + spawn(fn -> free_pool() end) + start_worker(opts, true) res -> res -- cgit v1.2.3 From 9ef021e2dae1a0bc07e997489304875b0d45ec07 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 20 Jun 2024 14:17:28 -0400 Subject: Switch the reclaimer to GenServer.start so it is not linked --- lib/pleroma/gun/connection_pool/reclaimer.ex | 2 +- lib/pleroma/gun/connection_pool/worker_supervisor.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex index 35e7f4b2e..3580d38f5 100644 --- a/lib/pleroma/gun/connection_pool/reclaimer.ex +++ b/lib/pleroma/gun/connection_pool/reclaimer.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do def start_monitor do pid = - case GenServer.start_link(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do + case GenServer.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do {:ok, pid} -> pid diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index dabb15b08..b9dedf61e 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do def start_worker(opts, false) do case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do {:error, :max_children} -> - spawn(fn -> free_pool() end) + free_pool() start_worker(opts, true) res -> -- cgit v1.2.3 From 634e3d4155df4c327d608a112fd4a3caf44cf9b8 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 23 Jun 2024 20:56:58 -0400 Subject: Add test validating the activity_id is correctly present in the Oban job This was preventing the activity from being streamed over websockets. --- lib/pleroma/web/rich_media/card.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/card.ex b/lib/pleroma/web/rich_media/card.ex index 72ff5e791..abad4957e 100644 --- a/lib/pleroma/web/rich_media/card.ex +++ b/lib/pleroma/web/rich_media/card.ex @@ -83,7 +83,7 @@ defmodule Pleroma.Web.RichMedia.Card do card nil -> - activity_id = Keyword.get(opts, :activity, nil) + activity_id = Keyword.get(opts, :activity_id, nil) RichMediaWorker.new(%{"op" => "backfill", "url" => url, "activity_id" => activity_id}) |> Oban.insert() -- cgit v1.2.3 From b135fa35a1e6258dbe1a07438213ce2593d86bc2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 24 Jun 2024 09:46:55 -0400 Subject: RichMedia: test that activity is streamed out --- lib/pleroma/web/rich_media/backfill.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index 1d8cc87d4..f1ee83bf0 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -11,6 +11,10 @@ defmodule Pleroma.Web.RichMedia.Backfill do require Logger @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @stream_out_impl Pleroma.Config.get( + [__MODULE__, :stream_out], + Pleroma.Web.ActivityPub.ActivityPub + ) @spec run(map()) :: :ok | {:error, {:invalid_metadata, any()} | :body_too_large | {:content, any()} | any()} @@ -64,7 +68,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do defp stream_update(%{"activity_id" => activity_id}) do Pleroma.Activity.get_by_id(activity_id) |> Pleroma.Activity.normalize() - |> Pleroma.Web.ActivityPub.ActivityPub.stream_out() + |> @stream_out_impl.stream_out() end defp warm_cache(key, val), do: @cachex.put(:rich_media_cache, key, val) -- cgit v1.2.3 From 51a36bc9b86738bcd9cf319d16077847a2381919 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 28 Jun 2024 10:12:58 -0400 Subject: Oban Jobs for refreshing users were not respecting the uniqueness setting --- lib/pleroma/workers/user_refresh_worker.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex index 5842143f8..f43170c8f 100644 --- a/lib/pleroma/workers/user_refresh_worker.ex +++ b/lib/pleroma/workers/user_refresh_worker.ex @@ -3,11 +3,11 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.UserRefreshWorker do - use Pleroma.Workers.WorkerHelper, queue: "background", max_attempts: 1, unique: [period: 300] + use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: 300] alias Pleroma.User - @impl Oban.Worker + @impl true def perform(%Job{args: %{"ap_id" => ap_id}}) do User.fetch_by_ap_id(ap_id) end -- cgit v1.2.3 From 9e5adf31c016e9702e0fddc5b945d7b095696a32 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 28 Jun 2024 10:22:46 -0400 Subject: Change Gun connection pool logs to debug --- lib/pleroma/telemetry/logger.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 9998d8185..31ce3cc20 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -39,7 +39,7 @@ defmodule Pleroma.Telemetry.Logger do _, _ ) do - Logger.error(fn -> + Logger.debug(fn -> "Connection pool failed to reclaim any connections due to all of them being in use. It will have to drop requests for opening connections to new hosts" end) end @@ -70,7 +70,7 @@ defmodule Pleroma.Telemetry.Logger do %{key: key}, _ ) do - Logger.warning(fn -> + Logger.debug(fn -> "Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{inspect(reason)}" end) end -- cgit v1.2.3 From 6d4fb532062c3c09187c81ef8af5f5b638ca4414 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 28 Jun 2024 21:59:00 +0400 Subject: StripLocation: Remove all PNG extra info to make sure that GPS data is gone. --- lib/pleroma/upload/filter/exiftool/strip_location.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/upload/filter/exiftool/strip_location.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex index 8becee712..1744a286d 100644 --- a/lib/pleroma/upload/filter/exiftool/strip_location.ex +++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex @@ -16,7 +16,9 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do try do - case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do + case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", "-png:all=", file], + parallelism: true + ) do {_response, 0} -> {:ok, :filtered} {error, 1} -> {:error, error} end -- cgit v1.2.3 From d3cccce9fd80d7359f7016cbe7e8796ba1fee3ab Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 27 Jun 2024 11:37:44 -0400 Subject: Elixir 1.17 warnings for parens Function calls must have parens --- lib/pleroma/search.ex | 2 +- lib/pleroma/web.ex | 2 +- lib/pleroma/web/activity_pub/mrf.ex | 2 +- lib/pleroma/web/api_spec/operations/streaming_operation.ex | 2 +- lib/pleroma/web/api_spec/schemas/chat.ex | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/search.ex b/lib/pleroma/search.ex index fd0218cb8..b9d2a0188 100644 --- a/lib/pleroma/search.ex +++ b/lib/pleroma/search.ex @@ -16,6 +16,6 @@ defmodule Pleroma.Search do def healthcheck_endpoints do search_module = Pleroma.Config.get([Pleroma.Search, :module]) - search_module.healthcheck_endpoints + search_module.healthcheck_endpoints() end end diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex index 7a8b176cd..e7e7e96f9 100644 --- a/lib/pleroma/web.ex +++ b/lib/pleroma/web.ex @@ -163,7 +163,7 @@ defmodule Pleroma.Web do """ def safe_render_many(collection, view, template, assigns \\ %{}) do Enum.map(collection, fn resource -> - as = Map.get(assigns, :as) || view.__resource__ + as = Map.get(assigns, :as) || view.__resource__() assigns = Map.put(assigns, as, resource) safe_render(view, template, assigns) end) diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 1071f8e6e..bc418d908 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -204,7 +204,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do if function_exported?(policy, :config_description, 0) do description = @default_description - |> Map.merge(policy.config_description) + |> Map.merge(policy.config_description()) |> Map.put(:group, :pleroma) |> Map.put(:tab, :mrf) |> Map.put(:type, :group) diff --git a/lib/pleroma/web/api_spec/operations/streaming_operation.ex b/lib/pleroma/web/api_spec/operations/streaming_operation.ex index b580bc2f0..47bce07b3 100644 --- a/lib/pleroma/web/api_spec/operations/streaming_operation.ex +++ b/lib/pleroma/web/api_spec/operations/streaming_operation.ex @@ -139,7 +139,7 @@ defmodule Pleroma.Web.ApiSpec.StreamingOperation do end defp get_schema(%Schema{} = schema), do: schema - defp get_schema(schema), do: schema.schema + defp get_schema(schema), do: schema.schema() defp server_sent_event_helper(name, description, type, payload, opts \\ []) do payload_type = Keyword.get(opts, :payload_type, :json) diff --git a/lib/pleroma/web/api_spec/schemas/chat.ex b/lib/pleroma/web/api_spec/schemas/chat.ex index a07d12865..affa25a95 100644 --- a/lib/pleroma/web/api_spec/schemas/chat.ex +++ b/lib/pleroma/web/api_spec/schemas/chat.ex @@ -68,7 +68,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Chat do }, "id" => "1", "unread" => 2, - "last_message" => ChatMessage.schema().example(), + "last_message" => ChatMessage.schema().example, "updated_at" => "2020-04-21T15:06:45.000Z" } }) -- cgit v1.2.3 From 6e1aa8aeeb3f8ba8469d80ee9872c32dee8f4205 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 27 Jun 2024 11:44:54 -0400 Subject: Elixir 1.17 undefined module warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes module name being not fully qualified warning: AdminAPI.FallbackController.call/2 is undefined (module AdminAPI.FallbackController is not available or is yet to be defined) │ 5 │ defmodule Pleroma.Web.AdminAPI.RuleController do │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ └─ lib/pleroma/web/admin_api/controllers/rule_controller.ex:5: Pleroma.Web.AdminAPI.RuleController.action/2 warning: AdminAPI.FallbackController.init/1 is undefined (module AdminAPI.FallbackController is not available or is yet to be defined) │ 5 │ defmodule Pleroma.Web.AdminAPI.RuleController do │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ └─ lib/pleroma/web/admin_api/controllers/rule_controller.ex:5: Pleroma.Web.AdminAPI.RuleController.action/2 --- lib/pleroma/web/admin_api/controllers/rule_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/controllers/rule_controller.ex b/lib/pleroma/web/admin_api/controllers/rule_controller.ex index 43b2f209a..5d4427b84 100644 --- a/lib/pleroma/web/admin_api/controllers/rule_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/rule_controller.ex @@ -24,7 +24,7 @@ defmodule Pleroma.Web.AdminAPI.RuleController do plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :index) - action_fallback(AdminAPI.FallbackController) + action_fallback(Pleroma.Web.AdminAPI.FallbackController) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.RuleOperation -- cgit v1.2.3 From fb52099a1301bb9dfd4f9cdeaf06b11b8420f2c2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 27 Jun 2024 11:57:18 -0400 Subject: Elixir 1.17 single quote charlist warning --- lib/pleroma/web/templates/feed/feed/tag.atom.eex | 2 +- lib/pleroma/web/templates/feed/feed/tag.rss.eex | 2 +- lib/pleroma/web/templates/feed/feed/user.atom.eex | 4 ++-- lib/pleroma/web/templates/feed/feed/user.rss.eex | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 14b0ee594..3449c97ff 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -12,7 +12,7 @@ <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> <%= feed_logo() %> <%= most_recent_update(@activities) %> - + " type="application/atom+xml"/> <%= for activity <- @activities do %> <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex index 27dde5627..a87f9bf50 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -6,7 +6,7 @@ #<%= @tag %> <%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %> - <%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %> + <%= "#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss" %> <%= feed_logo() %> 2b90d9 <%= for activity <- @activities do %> diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex index e36bfc66c..c2c77cfed 100644 --- a/lib/pleroma/web/templates/feed/feed/user.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex @@ -11,12 +11,12 @@ <%= escape(@user.bio) %> <%= most_recent_update(@activities, @user, :atom) %> <%= logo(@user) %> - + " type="application/atom+xml"/> <%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %> <%= if last_activity(@activities) do %> - + " type="application/atom+xml"/> <% end %> <%= for activity <- @activities do %> diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex index fae3fcf3d..b907a7e57 100644 --- a/lib/pleroma/web/templates/feed/feed/user.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex @@ -7,20 +7,20 @@ xmlns:poco="http://portablecontacts.net/spec/1.0"> <%= @user.nickname <> "'s timeline" %> - <%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %> + <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %> " rel="self" type="application/rss+xml" /> <%= escape(@user.bio) %> <%= logo(@user) %> <%= @user.nickname <> "'s timeline" %> - <%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %> + <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss" %> <%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %> <%= if last_activity(@activities) do %> - <%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %> + <%= "#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}" %> <% end %> <%= for activity <- @activities do %> -- cgit v1.2.3 From 69482004fc68174286e44010c816003aaa8868ce Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 10:58:22 -0400 Subject: Dialyzer: pattern can never match the type because it is covered by previous clauses. --- lib/pleroma/frontend.ex | 4 ---- lib/pleroma/web/activity_pub/activity_pub.ex | 1 - lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex | 1 - lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 3 --- lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex | 1 - lib/pleroma/web/admin_api/controllers/invite_controller.ex | 1 - lib/pleroma/web/common_api.ex | 1 - lib/pleroma/web/twitter_api/controllers/util_controller.ex | 3 --- 8 files changed, 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex index ec72fb6a4..816499917 100644 --- a/lib/pleroma/frontend.ex +++ b/lib/pleroma/frontend.ex @@ -43,10 +43,6 @@ defmodule Pleroma.Frontend do {:download_or_unzip, _} -> Logger.info("Could not download or unzip the frontend") {:error, "Could not download or unzip the frontend"} - - _e -> - Logger.info("Could not install the frontend") - {:error, "Could not install the frontend"} end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 6c1d6ded9..b30b0cabe 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1661,7 +1661,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do }} else {:error, _} = e -> e - e -> {:error, e} end end diff --git a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex index 3d1c273b9..451a212d4 100644 --- a/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex @@ -137,7 +137,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do {:ok, object} else {:nsfw, _data} -> handle_nsfw(object) - _ -> {:reject, "NSFW: Attachment rejected"} end end diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 829ddeaea..8e3021ae6 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -222,7 +222,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do else {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e - _ -> {:reject, "[SimplePolicy]"} end end @@ -238,7 +237,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do else {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e - _ -> {:reject, "[SimplePolicy]"} end end @@ -251,7 +249,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do else {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e - _ -> {:reject, "[SimplePolicy]"} end end diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index d9deff35f..1c114558e 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -31,7 +31,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do {:reject, _} = e -> e {:accepted, _} -> {:reject, "[VocabularyPolicy] #{message_type} not in accept list"} {:rejected, _} -> {:reject, "[VocabularyPolicy] #{message_type} in reject list"} - _ -> {:reject, "[VocabularyPolicy]"} end end diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex index 7e3020f28..30dbc7e73 100644 --- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex @@ -46,7 +46,6 @@ defmodule Pleroma.Web.AdminAPI.InviteController do render(conn, "show.json", invite: updated_invite) else nil -> {:error, :not_found} - error -> error end end diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 34e480d73..7cefe3647 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -611,7 +611,6 @@ defmodule Pleroma.Web.CommonAPI do Utils.update_report_state(activity, state) else nil -> {:error, :not_found} - _ -> {:error, dgettext("errors", "Could not update state")} end end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 040fa3286..6805233df 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -207,9 +207,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do {:error, changeset} -> {_, {error, _}} = Enum.at(changeset.errors, 0) json(conn, %{error: "New password #{error}."}) - - _ -> - json(conn, %{error: "Unable to change password."}) end {:error, msg} -> -- cgit v1.2.3 From a008005bdd6ed373c8984935801903ca3a9c409e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 11:14:39 -0400 Subject: Dialyzer: fix typespec --- lib/pleroma/web/activity_pub/pipeline.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index 40184bd97..7f11a4d67 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do defp config, do: Config.get([:pipeline, :config], Config) @spec common_pipeline(map(), keyword()) :: - {:ok, Activity.t() | Object.t(), keyword()} | {:error, any()} + {:ok, Activity.t() | Object.t(), keyword()} | {:error | :reject, any()} def common_pipeline(object, meta) do case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do {:ok, {:ok, activity, meta}} -> -- cgit v1.2.3 From fd62969dc49e7e7be9a87813296232e8506d2c56 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 11:17:14 -0400 Subject: Dialyzer: pattern can never match the type --- lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 3 --- lib/pleroma/web/common_api.ex | 1 - 2 files changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 8e3021ae6..d708c99eb 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -220,7 +220,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do {:ok, object} <- check_object(object) do {:ok, object} else - {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e end end @@ -235,7 +234,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do {:ok, object} <- check_banner_removal(actor_info, object) do {:ok, object} else - {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e end end @@ -247,7 +245,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do {:ok, object} <- check_reject(uri, object) do {:ok, object} else - {:reject, nil} -> {:reject, "[SimplePolicy]"} {:reject, _} = e -> e end end diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 7cefe3647..09bedcd2b 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -620,7 +620,6 @@ defmodule Pleroma.Web.CommonAPI do set_visibility(activity, opts) else nil -> {:error, :not_found} - {:error, reason} -> {:error, reason} end end -- cgit v1.2.3 From 7955cd90e6761476fb4a68789aca0c3a4955bbfa Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 11:19:15 -0400 Subject: Dialyzer: The guard clause can never succeed. --- lib/pleroma/application_requirements.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index 8c0df64fc..a334d12ee 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -241,10 +241,9 @@ defmodule Pleroma.ApplicationRequirements do missing_mrfs = Enum.reduce(mrfs, [], fn x, acc -> - if Code.ensure_compiled(x) do - acc - else - acc ++ [x] + case Code.ensure_compiled(x) do + {:module, _} -> acc + {:error, _} -> acc ++ [x] end end) -- cgit v1.2.3 From b283b686c41a997b99047db9a8ccbe015d167b76 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 11:35:23 -0400 Subject: Dialyzer: Function application with args (_ :: map()) will not succeed. --- lib/pleroma/object.ex | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 55b646b12..eb44b3855 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -99,21 +99,24 @@ 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 - %{updated_at: updated_at} = object = get_by_id(id) - - 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 + 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 - object + nil -> nil end end -- cgit v1.2.3 From 94027894373f690ad35300ab3ba1a67867d957cd Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 11:47:27 -0400 Subject: Dialyzer: fix typespec --- lib/pleroma/web/o_auth/app.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 0aa655381..d1bf6dd18 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -62,7 +62,7 @@ defmodule Pleroma.Web.OAuth.App do |> Repo.insert() end - @spec update(pos_integer(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} + @spec update(pos_integer(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} | nil def update(id, params) do with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do app -- cgit v1.2.3 From 3127c5f0af3f62fb4c0eca642e69ede8f395d9b4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 1 Jul 2024 15:58:15 -0400 Subject: Fix automatic LDAP account registration on OTP 24.3+ --- lib/pleroma/web/auth/ldap_authenticator.ex | 45 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 17ffd820d..c2c5eb1e5 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -102,28 +102,37 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do {:scope, :eldap.wholeSubtree()}, {:timeout, @search_timeout} ]) do - {:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} -> - params = %{ - name: name, - nickname: name, - password: nil - } - - params = - case List.keyfind(attributes, ~c"mail", 0) do - {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) - _ -> params - end - - changeset = User.register_changeset_ldap(%User{}, params) + # The :eldap_search_result record structure changed in OTP 24.3 and added a controls field + # https://github.com/erlang/otp/pull/5538 + {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} -> + try_register(name, attributes) - case User.register(changeset) do - {:ok, user} -> user - error -> error - end + {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} -> + try_register(name, attributes) error -> error end end + + defp try_register(name, attributes) do + params = %{ + name: name, + nickname: name, + password: nil + } + + params = + case List.keyfind(attributes, ~c"mail", 0) do + {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) + _ -> params + end + + changeset = User.register_changeset_ldap(%User{}, params) + + case User.register(changeset) do + {:ok, user} -> user + error -> error + end + end end -- cgit v1.2.3 From 272aae157eee8628b830cc1818f39c8fa8884e23 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 6 Jul 2024 16:56:49 -0400 Subject: Refactor maybe_handle_group_posts/1 --- lib/pleroma/web/activity_pub/utils.ex | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 797e79dda..6c792804d 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -939,26 +939,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Repo.all() end + @spec maybe_handle_group_posts(Activity.t()) :: :ok + @doc "Automatically repeats posts for local group actor recipients" def maybe_handle_group_posts(activity) do poster = User.get_cached_by_ap_id(activity.actor) - mentions = - activity.data["to"] - |> Enum.filter(&(&1 != activity.actor)) - - mentioned_local_groups = - User.get_all_by_ap_id(mentions) - |> Enum.filter(fn user -> - user.actor_type == "Group" and - user.local and - not User.blocks?(user, poster) - end) - - mentioned_local_groups - |> Enum.each(fn group -> - Pleroma.Web.CommonAPI.repeat(activity.id, group) - end) - - :ok + User.get_recipients_from_activity(activity) + |> Enum.filter(&match?("Group", &1.actor_type)) + |> Enum.reject(&User.blocks?(&1, poster)) + |> Enum.each(&Pleroma.Web.CommonAPI.repeat(activity.id, &1)) end end -- cgit v1.2.3 From 03c3c58d74217edd0822b68772eb76a59451fe50 Mon Sep 17 00:00:00 2001 From: Taylan Kammer Date: Wed, 10 Jul 2024 07:21:37 +0200 Subject: LDAP Authenticator: Improve error reporting. --- lib/pleroma/web/auth/ldap_authenticator.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index c2c5eb1e5..ea5620cf6 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -91,7 +91,8 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do end error -> - error + Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}") + {:error, {:ldap_bind_error, error}} end end @@ -111,7 +112,8 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do try_register(name, attributes) error -> - error + Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}") + {:error, {:ldap_search_error, error}} end end -- cgit v1.2.3 From b051e68bb025417738579b027e4b039ffb9b19da Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 12 Jul 2024 10:15:02 -0400 Subject: Discard Remote Fetcher jobs which errored due to an MRF rejection --- lib/pleroma/workers/remote_fetcher_worker.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index ed04c54b2..debbbe012 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -13,6 +13,9 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do {:ok, _object} -> :ok + {:rejected, reason} -> + {:discard, reason} + {:error, :forbidden} -> {:discard, :forbidden} -- cgit v1.2.3 From f52b229baad212a052ae756ba89a51fad23049d3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 12 Jul 2024 11:33:38 -0400 Subject: Oban: change :discard return values to :cancel :discard will be removed in Oban 3.0. It was only meant for internal use. --- lib/pleroma/web/activity_pub/publisher.ex | 6 +++--- lib/pleroma/workers/remote_fetcher_worker.ex | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index a42b4844e..90c4274f2 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -123,9 +123,9 @@ defmodule Pleroma.Web.ActivityPub.Publisher do Logger.error("Publisher failed to inbox #{inbox} with status #{code}") case response do - %{status: 403} -> {:discard, :forbidden} - %{status: 404} -> {:discard, :not_found} - %{status: 410} -> {:discard, :not_found} + %{status: 403} -> {:cancel, :forbidden} + %{status: 404} -> {:cancel, :not_found} + %{status: 410} -> {:cancel, :not_found} _ -> {:error, e} end diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index debbbe012..6d777ae94 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -14,16 +14,16 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do :ok {:rejected, reason} -> - {:discard, reason} + {:cancel, reason} {:error, :forbidden} -> - {:discard, :forbidden} + {:cancel, :forbidden} {:error, :not_found} -> - {:discard, :not_found} + {:cancel, :not_found} {:error, :allowed_depth} -> - {:discard, :allowed_depth} + {:cancel, :allowed_depth} {:error, _} = e -> e -- cgit v1.2.3 From 1e8d1904e61c73f624bdce600fc243dff81e19fc Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 15 Jul 2024 10:15:23 -0400 Subject: Define missing Oban timeouts --- lib/pleroma/workers/cron/digest_emails_worker.ex | 3 +++ lib/pleroma/workers/cron/new_users_digest_worker.ex | 3 +++ lib/pleroma/workers/rich_media_worker.ex | 3 +++ lib/pleroma/workers/search_indexing_worker.ex | 3 +++ lib/pleroma/workers/user_refresh_worker.ex | 3 +++ 5 files changed, 15 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index 0292bbb3b..17e92d10b 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -58,4 +58,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do User.touch_last_digest_emailed_at(user) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index d2abb2d3b..1f57aad4a 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -60,4 +60,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do :ok end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index f18ac658a..ecc980a28 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -16,4 +16,7 @@ defmodule Pleroma.Workers.RichMediaWorker do def perform(%Job{args: %{"op" => "backfill", "url" => _url} = args}) do Backfill.run(args) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/search_indexing_worker.ex b/lib/pleroma/workers/search_indexing_worker.ex index 8476a2be5..8969ae378 100644 --- a/lib/pleroma/workers/search_indexing_worker.ex +++ b/lib/pleroma/workers/search_indexing_worker.ex @@ -20,4 +20,7 @@ defmodule Pleroma.Workers.SearchIndexingWorker do search_module.remove_from_index(object) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex index f43170c8f..0c04fb237 100644 --- a/lib/pleroma/workers/user_refresh_worker.ex +++ b/lib/pleroma/workers/user_refresh_worker.ex @@ -11,4 +11,7 @@ defmodule Pleroma.Workers.UserRefreshWorker do def perform(%Job{args: %{"ap_id" => ap_id}}) do User.fetch_by_ap_id(ap_id) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end -- cgit v1.2.3 From 2e2caad28db9dbc7342ac706bc743ec393c2e7e4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 15 Jul 2024 10:23:03 -0400 Subject: Fix Oban jobs exiting with :error instead of :cancel --- lib/pleroma/workers/purge_expired_activity.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index a65593b6e..6b0799a14 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -46,13 +46,13 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do defp find_activity(id) do with nil <- Activity.get_by_id_with_object(id) do - {:error, :activity_not_found} + {:cancel, :activity_not_found} end end defp find_user(ap_id) do with nil <- Pleroma.User.get_by_ap_id(ap_id) do - {:error, :user_not_found} + {:cancel, :user_not_found} end end -- cgit v1.2.3 From 2f14990c5c57df00126ea2b06ccb706c6dd8f918 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 15 Jul 2024 10:25:18 -0400 Subject: Change PurgeExpiredActivity to use the background queue --- lib/pleroma/workers/purge_expired_activity.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index 6b0799a14..f48e34042 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -6,8 +6,8 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do @moduledoc """ Worker which purges expired activity. """ - - use Oban.Worker, queue: :slow, max_attempts: 1, unique: [period: :infinity] + @queue :background + use Oban.Worker, queue: @queue, max_attempts: 1, unique: [period: :infinity] import Ecto.Query @@ -57,9 +57,11 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do end def get_expiration(id) do + queue = Atom.to_string(@queue) + from(j in Oban.Job, where: j.state == "scheduled", - where: j.queue == "slow", + where: j.queue == ^queue, where: fragment("?->>'activity_id' = ?", j.args, ^id) ) |> Pleroma.Repo.one() -- cgit v1.2.3 From 30defb1674512263e2eea25ef4a44d7159e8bb60 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 15 Jul 2024 11:58:07 -0400 Subject: Create a DeleteWorker and change user and instance deletion jobs to use it These deletion tasks are slow, but the other background jobs are not. This will allow us to have a lower timeout on the normal background jobs. --- lib/pleroma/instances/instance.ex | 4 ++-- lib/pleroma/user.ex | 3 ++- lib/pleroma/workers/background_worker.ex | 12 +----------- lib/pleroma/workers/delete_worker.ex | 24 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 lib/pleroma/workers/delete_worker.ex (limited to 'lib') diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index c497a4fb7..288555146 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Instances.Instance do alias Pleroma.Maps alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Workers.BackgroundWorker + alias Pleroma.Workers.DeleteWorker use Ecto.Schema @@ -297,7 +297,7 @@ defmodule Pleroma.Instances.Instance do all of those users' activities and notifications. """ def delete_users_and_activities(host) when is_binary(host) do - BackgroundWorker.enqueue("delete_instance", %{"host" => host}) + DeleteWorker.enqueue("delete_instance", %{"host" => host}) end def perform(:delete_instance, host) when is_binary(host) do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7a8a68931..e28d76a7c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -38,6 +38,7 @@ defmodule Pleroma.User do alias Pleroma.Web.OAuth alias Pleroma.Web.RelMe alias Pleroma.Workers.BackgroundWorker + alias Pleroma.Workers.DeleteWorker alias Pleroma.Workers.UserRefreshWorker require Logger @@ -1982,7 +1983,7 @@ defmodule Pleroma.User do def delete(%User{} = user) do # Purge the user immediately purge(user) - BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id}) + DeleteWorker.enqueue("delete_user", %{"user_id" => user.id}) end # *Actually* delete the user from the DB diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index dbf40ee1b..d74cc08f1 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackgroundWorker do - alias Pleroma.Instances.Instance alias Pleroma.User use Pleroma.Workers.WorkerHelper, queue: "background" @@ -15,11 +14,6 @@ defmodule Pleroma.Workers.BackgroundWorker do User.perform(:set_activation_async, user, status) end - def perform(%Job{args: %{"op" => "delete_user", "user_id" => user_id}}) do - user = User.get_cached_by_id(user_id) - User.perform(:delete, user) - end - def perform(%Job{args: %{"op" => "force_password_reset", "user_id" => user_id}}) do user = User.get_cached_by_id(user_id) User.perform(:force_password_reset, user) @@ -45,10 +39,6 @@ defmodule Pleroma.Workers.BackgroundWorker do User.perform(:verify_fields_links, user) end - def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do - Instance.perform(:delete_instance, host) - end - @impl Oban.Worker - def timeout(_job), do: :timer.seconds(900) + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/delete_worker.ex b/lib/pleroma/workers/delete_worker.ex new file mode 100644 index 000000000..97003fb69 --- /dev/null +++ b/lib/pleroma/workers/delete_worker.ex @@ -0,0 +1,24 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.DeleteWorker do + alias Pleroma.Instances.Instance + alias Pleroma.User + + use Pleroma.Workers.WorkerHelper, queue: "slow" + + @impl Oban.Worker + + def perform(%Job{args: %{"op" => "delete_user", "user_id" => user_id}}) do + user = User.get_cached_by_id(user_id) + User.perform(:delete, user) + end + + def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do + Instance.perform(:delete_instance, host) + end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(900) +end -- cgit v1.2.3 From c9203f125c3ce44bc7b7273fedca089aadfb6b86 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 15 Jul 2024 15:21:16 -0400 Subject: Added a Mix task "pleroma.config fix_mrf_policies" which will remove erroneous MRF policies from ConfigDB --- lib/mix/tasks/pleroma/config.ex | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 3a2ea44f8..8b3b2f18b 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -205,6 +205,35 @@ defmodule Mix.Tasks.Pleroma.Config do end end + # Removes any policies that are not a real module + # as they will prevent the server from starting + def run(["fix_mrf_policies"]) do + check_configdb(fn -> + start_pleroma() + + group = :pleroma + key = :mrf + + %{value: value} = + group + |> ConfigDB.get_by_group_and_key(key) + + policies = + Keyword.get(value, :policies, []) + |> Enum.filter(&is_atom(&1)) + |> Enum.filter(fn mrf -> + case Code.ensure_compiled(mrf) do + {:module, _} -> true + {:error, _} -> false + end + end) + + value = Keyword.put(value, :policies, policies) + + ConfigDB.update_or_create(%{group: group, key: key, value: value}) + end) + end + @spec migrate_to_db(Path.t() | nil) :: any() def migrate_to_db(file_path \\ nil) do with :ok <- Pleroma.Config.DeprecationWarnings.warn() do -- cgit v1.2.3 From 4cbb59c8f615183efa67ad8b628fce40c6bf7cd9 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 17 Jul 2024 09:32:29 -0400 Subject: Add Oban Live Dashboard --- lib/pleroma/web/router.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 56c457e90..fc40a1143 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -988,7 +988,7 @@ defmodule Pleroma.Web.Router do scope "/" do pipe_through([:pleroma_html, :authenticate, :require_admin]) - live_dashboard("/phoenix/live_dashboard") + live_dashboard("/phoenix/live_dashboard", additional_pages: [oban: Oban.LiveDashboard]) end # Test-only routes needed to test action dispatching and plug chain execution -- cgit v1.2.3 From d124d8645e1455fe5d4d2ab143f3ca09967f9572 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 17 Jul 2024 12:40:07 -0400 Subject: Rework some Rich Media functionality for better error handling Oban should not retry jobs that are likely to fail again --- lib/pleroma/web/rich_media/backfill.ex | 24 ++++++--------- lib/pleroma/web/rich_media/helpers.ex | 44 +++++++++++++++++---------- lib/pleroma/web/rich_media/parser.ex | 23 ++++++++------ lib/pleroma/web/rich_media/parsers/o_embed.ex | 2 +- lib/pleroma/workers/rich_media_worker.ex | 12 +++++++- 5 files changed, 62 insertions(+), 43 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index f1ee83bf0..a66422e71 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do alias Pleroma.Web.RichMedia.Card + alias Pleroma.Web.RichMedia.Helpers alias Pleroma.Web.RichMedia.Parser alias Pleroma.Web.RichMedia.Parser.TTL alias Pleroma.Workers.RichMediaWorker @@ -16,8 +17,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do Pleroma.Web.ActivityPub.ActivityPub ) - @spec run(map()) :: - :ok | {:error, {:invalid_metadata, any()} | :body_too_large | {:content, any()} | any()} + @spec run(map()) :: :ok | Parser.parse_errors() | Helpers.get_errors() def run(%{"url" => url} = args) do url_hash = Card.url_to_hash(url) @@ -33,22 +33,16 @@ defmodule Pleroma.Web.RichMedia.Backfill do end warm_cache(url_hash, card) + :ok - {:error, {:invalid_metadata, fields}} -> - Logger.debug("Rich media incomplete or invalid metadata for #{url}: #{inspect(fields)}") - negative_cache(url_hash) - - {:error, :body_too_large} -> - Logger.error("Rich media error for #{url}: :body_too_large") - negative_cache(url_hash) - - {:error, {:content_type, type}} -> - Logger.debug("Rich media error for #{url}: :content_type is #{type}") + {:error, type} = error + when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> negative_cache(url_hash) + error - e -> - Logger.debug("Rich media error for #{url}: #{inspect(e)}") - {:error, e} + {:error, type} = error + when type in [:get, :head] -> + error end end diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index ea41bd285..fba23c657 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -5,26 +5,38 @@ defmodule Pleroma.Web.RichMedia.Helpers do alias Pleroma.Config + require Logger + + @type get_errors :: {:error, :body_too_large | :content_type | :head | :get} + + @spec rich_media_get(String.t()) :: {:ok, String.t()} | get_errors() def rich_media_get(url) do headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}] - head_check = - case Pleroma.HTTP.head(url, headers, http_options()) do - # If the HEAD request didn't reach the server for whatever reason, - # we assume the GET that comes right after won't either - {:error, _} = e -> - e + with {_, {:ok, %Tesla.Env{status: 200, headers: headers}}} <- + {:head, Pleroma.HTTP.head(url, headers, http_options())}, + {_, :ok} <- {:content_type, check_content_type(headers)}, + {_, :ok} <- {:content_length, check_content_length(headers)}, + {_, {:ok, %Tesla.Env{status: 200, body: body}}} <- + {:get, Pleroma.HTTP.get(url, headers, http_options())} do + {:ok, body} + else + {:head, _} -> + Logger.debug("Rich media error for #{url}: HTTP HEAD failed") + {:error, :head} - {:ok, %Tesla.Env{status: 200, headers: headers}} -> - with :ok <- check_content_type(headers), - :ok <- check_content_length(headers), - do: :ok + {:content_type, {_, type}} -> + Logger.debug("Rich media error for #{url}: content-type is #{type}") + {:error, :content_type} - _ -> - :ok - end + {:content_length, {_, length}} -> + Logger.debug("Rich media error for #{url}: content-length is #{length}") + {:error, :body_too_large} - with :ok <- head_check, do: Pleroma.HTTP.get(url, headers, http_options()) + {:get, _} -> + Logger.debug("Rich media error for #{url}: HTTP GET failed") + {:error, :get} + end end defp check_content_type(headers) do @@ -32,7 +44,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do {_, content_type} -> case Plug.Conn.Utils.media_type(content_type) do {:ok, "text", "html", _} -> :ok - _ -> {:error, {:content_type, content_type}} + _ -> {:error, content_type} end _ -> @@ -47,7 +59,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do {_, maybe_content_length} -> case Integer.parse(maybe_content_length) do {content_length, ""} when content_length <= max_body -> :ok - {_, ""} -> {:error, :body_too_large} + {_, ""} -> {:error, maybe_content_length} _ -> :ok end diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 7f6b5d388..a3a522d7a 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser do + alias Pleroma.Web.RichMedia.Helpers require Logger @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) @@ -11,24 +12,26 @@ defmodule Pleroma.Web.RichMedia.Parser do Pleroma.Config.get([:rich_media, :parsers]) end - def parse(nil), do: nil + @type parse_errors :: {:error, :rich_media_disabled | :validate} - @spec parse(String.t()) :: {:ok, map()} | {:error, any()} - def parse(url) do + @spec parse(String.t()) :: + {:ok, map()} | parse_errors() | Helpers.get_errors() + def parse(url) when is_binary(url) do with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])}, - :ok <- validate_page_url(url), - {:ok, data} <- parse_url(url) do + {_, :ok} <- {:validate, validate_page_url(url)}, + {_, {:ok, data}} <- {:parse, parse_url(url)} do data = Map.put(data, "url", url) {:ok, data} else {:config, _} -> {:error, :rich_media_disabled} - e -> e + {:validate, _} -> {:error, :validate} + {:parse, error} -> error end end defp parse_url(url) do - with {:ok, %Tesla.Env{body: html}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url), - {:ok, html} <- Floki.parse_document(html) do + with {:ok, body} <- Helpers.rich_media_get(url), + {:ok, html} <- Floki.parse_document(body) do html |> maybe_parse() |> clean_parsed_data() @@ -50,8 +53,8 @@ defmodule Pleroma.Web.RichMedia.Parser do {:ok, data} end - defp check_parsed_data(data) do - {:error, {:invalid_metadata, data}} + defp check_parsed_data(_data) do + {:error, :invalid_metadata} end defp clean_parsed_data(data) do diff --git a/lib/pleroma/web/rich_media/parsers/o_embed.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex index 0f303176c..35ec14426 100644 --- a/lib/pleroma/web/rich_media/parsers/o_embed.ex +++ b/lib/pleroma/web/rich_media/parsers/o_embed.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do end defp get_oembed_data(url) do - with {:ok, %Tesla.Env{body: json}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do + with {:ok, json} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do Jason.decode(json) end end diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index ecc980a28..0db97633f 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -14,7 +14,17 @@ defmodule Pleroma.Workers.RichMediaWorker do end def perform(%Job{args: %{"op" => "backfill", "url" => _url} = args}) do - Backfill.run(args) + case Backfill.run(args) do + :ok -> + :ok + + {:error, type} + when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> + :cancel + + error -> + {:error, error} + end end @impl Oban.Worker -- cgit v1.2.3 From 1e0d5934d53f95ac7737e844cf060d2cfe1be98d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 17 Jul 2024 12:51:38 -0400 Subject: Fix return for cancelling job --- lib/pleroma/workers/rich_media_worker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index 0db97633f..a94a85ec7 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Workers.RichMediaWorker do {:error, type} when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> - :cancel + {:cancel, type} error -> {:error, error} -- cgit v1.2.3 From f753bd33805159a08cc8b447daa1c9ace137deab Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 17 Jul 2024 13:12:51 -0400 Subject: Explicitly handle the GET and HEAD errors --- lib/pleroma/workers/rich_media_worker.ex | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index a94a85ec7..30f9d9e9e 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -22,6 +22,10 @@ defmodule Pleroma.Workers.RichMediaWorker do when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> {:cancel, type} + {:error, type} + when type in [:get, :head] -> + {:error, type} + error -> {:error, error} end -- cgit v1.2.3 From c05cbaa9375660572ea9c720fa1825ab4c8b325c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 17 Jul 2024 13:42:13 -0400 Subject: Dialyzer fix for RemoteFetcherWorker --- lib/pleroma/object/fetcher.ex | 1 + lib/pleroma/workers/remote_fetcher_worker.ex | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index af5642af4..c0f671dd4 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -59,6 +59,7 @@ defmodule Pleroma.Object.Fetcher do end # Note: will create a Create activity, which we need internally at the moment. + @spec fetch_object_from_id(String.t(), list()) :: {:ok, Object.t()} | {:error | :reject, any()} def fetch_object_from_id(id, options \\ []) do with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)}, {_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])}, diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index 6d777ae94..1c8874b59 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do {:ok, _object} -> :ok - {:rejected, reason} -> + {:reject, reason} -> {:cancel, reason} {:error, :forbidden} -> @@ -27,9 +27,6 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do {:error, _} = e -> e - - e -> - {:error, e} end end -- cgit v1.2.3 From 1f3ac66844e3a5f3e85aa0fb2afd23a2bc706f8b Mon Sep 17 00:00:00 2001 From: Mint Date: Thu, 18 Jul 2024 18:00:40 +0300 Subject: Transmogrifier: handle non-validate errors on incoming Delete activities This should fix WithClauseError resulting in Oban jobs for processing incoming deletes being retried without getting cancelled when those deletes are MRF rejected. --- lib/pleroma/web/activity_pub/transmogrifier.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 4d851559f..9b6ecb3a9 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -530,6 +530,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do else _ -> e end + e -> {:error, e} end end -- cgit v1.2.3 From d3c2180181c39fdfd52691ffcaf289b86de20a71 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 18 Jul 2024 12:12:34 -0400 Subject: Formatting --- lib/pleroma/web/activity_pub/transmogrifier.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 9b6ecb3a9..2f8a7f8f2 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -530,7 +530,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do else _ -> e end - e -> {:error, e} + + e -> + {:error, e} end end -- cgit v1.2.3 From 62280a3b9f556f7eb5c2940b0ddc20c79fbcc758 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 13:32:56 -0400 Subject: Cancel queued (undelivered) publishing jobs for an activity when deleting that activity. --- lib/pleroma/web/common_api.ex | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 09bedcd2b..d43c46520 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI.ActivityDraft + import Ecto.Query, only: [where: 3] import Pleroma.Web.Gettext import Pleroma.Web.CommonAPI.Utils @@ -156,6 +157,7 @@ defmodule Pleroma.Web.CommonAPI do def delete(activity_id, user) do with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(activity_id, filter: [])}, + {_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(activity)}, {_, %Object{} = object, _} <- {:find_object, Object.normalize(activity, fetch: false), activity}, true <- User.privileged?(user, :messages_delete) || user.ap_id == object.data["actor"], @@ -671,4 +673,14 @@ defmodule Pleroma.Web.CommonAPI do nil end end + + defp maybe_cancel_jobs(%Activity{data: %{"id" => ap_id}}) do + Oban.Job + |> where([j], j.worker == "Pleroma.Workers.PublisherWorker") + |> where([j], j.args["op"] == "publish_one") + |> where([j], j.args["params"]["id"] == ^ap_id) + |> Oban.cancel_all_jobs() + end + + defp maybe_cancel_jobs(_), do: {:ok, 0} end -- cgit v1.2.3 From 86ae00f9da4d9e39d8f635d51b1139529b6fb9dc Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 14:53:53 -0400 Subject: Support cancelling jobs when Unfavoriting --- lib/pleroma/web/common_api.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index d43c46520..9f730d811 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -277,6 +277,7 @@ defmodule Pleroma.Web.CommonAPI do {:find_activity, Activity.get_by_id(id)}, %Object{} = note <- Object.normalize(activity, fetch: false), %Activity{} = like <- Utils.get_existing_like(user.ap_id, note), + {_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(like)}, {:ok, undo, _} <- Builder.undo(user, like), {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do {:ok, activity} -- cgit v1.2.3 From 304b7f5093c7e4ea096a3fec85e0c9339f745db0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 15:06:19 -0400 Subject: Support cancelling jobs when Unrepeating --- lib/pleroma/web/common_api.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 9f730d811..de3ec85fe 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -225,6 +225,7 @@ defmodule Pleroma.Web.CommonAPI do {:find_activity, Activity.get_by_id(id)}, %Object{} = note <- Object.normalize(activity, fetch: false), %Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note), + {_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(announce)}, {:ok, undo, _} <- Builder.undo(user, announce), {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do {:ok, activity} -- cgit v1.2.3 From d44765bc1303bd4b6fcb066197ccf66b758cdc99 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 15:14:46 -0400 Subject: Support cancelling jobs when Unreacting --- lib/pleroma/web/common_api.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index de3ec85fe..36e7efd8d 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -302,6 +302,7 @@ defmodule Pleroma.Web.CommonAPI do def unreact_with_emoji(id, user, emoji) do with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji), + {_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(reaction_activity)}, {:ok, undo, _} <- Builder.undo(user, reaction_activity), {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do {:ok, activity} -- cgit v1.2.3 From e509519db4fe6c9c0c3942ebd9ccb540439fba18 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 21:21:21 -0400 Subject: Publisher jobs will not retry if the error received is a 400 --- lib/pleroma/web/activity_pub/publisher.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 90c4274f2..c8bdf2250 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -123,6 +123,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do Logger.error("Publisher failed to inbox #{inbox} with status #{code}") case response do + %{status: 400} -> {:cancel, :bad_request} %{status: 403} -> {:cancel, :forbidden} %{status: 404} -> {:cancel, :not_found} %{status: 410} -> {:cancel, :not_found} -- cgit v1.2.3 From b8503f1ad412df72af2dc2d975d29e8810143667 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 21:24:13 -0400 Subject: PollWorker jobs will not retry if the activity no longer exists. --- lib/pleroma/workers/poll_worker.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 3fcac9bc3..af8997e70 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -17,6 +17,9 @@ defmodule Pleroma.Workers.PollWorker do with %Activity{} = activity <- find_poll_activity(activity_id), {:ok, notifications} <- Notification.create_poll_notifications(activity) do Notification.stream(notifications) + else + {:error, :poll_activity_not_found} = e -> {:cancel, e} + e -> {:error, e} end end -- cgit v1.2.3 From d62a9afed3fe08aeaf9d2d9f0ca79736a1f24578 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 21:52:57 -0400 Subject: Improved detecting unrecoverable errors for incoming federation jobs --- lib/pleroma/workers/receiver_worker.ex | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 8b2052c23..cf1269a1f 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -47,11 +47,13 @@ defmodule Pleroma.Workers.ReceiverWorker do case errors do {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} {:error, :already_present} -> {:cancel, :already_present} - {:error, {:validate_object, reason}} -> {:cancel, reason} - {:error, {:error, {:validate, reason}}} -> {:cancel, reason} - {:error, {:reject, reason}} -> {:cancel, reason} + {:error, {:validate_object, _} = reason} -> {:cancel, reason} + {:error, {:validate, _} = reason} -> {:cancel, reason} + {:error, {:reject, _} = reason} -> {:cancel, reason} {:signature, false} -> {:cancel, :invalid_signature} - {:error, {:error, reason = "Object has been deleted"}} -> {:cancel, reason} + {:error, "Object has been deleted"} = reason -> {:cancel, reason} + {:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason} + {:error, :not_found} = reason -> {:cancel, reason} {:error, _} = e -> e e -> {:error, e} end -- cgit v1.2.3 From fdeb8616e8452d78060fb41622688b7143687058 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 22:00:34 -0400 Subject: Increase timeout for background, remote fetcher, and user refresh jobs --- lib/pleroma/web/activity_pub/side_effects.ex | 2 +- lib/pleroma/workers/background_worker.ex | 2 +- lib/pleroma/workers/remote_fetcher_worker.ex | 2 +- lib/pleroma/workers/user_refresh_worker.ex | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 2a141b0f5..cc1c7a0af 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -453,7 +453,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do ) do orig_object_ap_id = updated_object["id"] orig_object = Object.get_by_ap_id(orig_object_ap_id) - orig_object_data = orig_object.data + orig_object_data = Map.get(orig_object, :data) updated_object = if meta[:local] do diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index d74cc08f1..870aef3c6 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -40,5 +40,5 @@ defmodule Pleroma.Workers.BackgroundWorker do end @impl Oban.Worker - def timeout(_job), do: :timer.seconds(5) + def timeout(_job), do: :timer.seconds(15) end diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index 1c8874b59..60096e14b 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -31,5 +31,5 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do end @impl Oban.Worker - def timeout(_job), do: :timer.seconds(10) + def timeout(_job), do: :timer.seconds(15) end diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex index 0c04fb237..fb90e9c9c 100644 --- a/lib/pleroma/workers/user_refresh_worker.ex +++ b/lib/pleroma/workers/user_refresh_worker.ex @@ -13,5 +13,5 @@ defmodule Pleroma.Workers.UserRefreshWorker do end @impl Oban.Worker - def timeout(_job), do: :timer.seconds(5) + def timeout(_job), do: :timer.seconds(15) end -- cgit v1.2.3 From fb654acfadfeec00a1a52e2af96e922dc4b88b01 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 20 Jul 2024 23:48:54 -0400 Subject: Fix OpenGraph and Twitter metadata providers when parsing objects with no content or summary fields. --- lib/pleroma/web/metadata/utils.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 80a8be9a2..8f61ace24 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -25,11 +25,14 @@ defmodule Pleroma.Web.Metadata.Utils do |> scrub_html_and_truncate_object_field(object) end - def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do + def scrub_html_and_truncate(%{data: %{"content" => content}} = object) + when is_binary(content) and content != "" do content |> scrub_html_and_truncate_object_field(object) end + def scrub_html_and_truncate(%{}), do: "" + def scrub_html_and_truncate(content, max_length \\ 200, omission \\ "...") when is_binary(content) do content -- cgit v1.2.3 From f9647a86ede93da9066671790f37234128669f0f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 21 Jul 2024 00:03:51 -0400 Subject: Fix the ObjectValidator error matching --- lib/pleroma/workers/receiver_worker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index cf1269a1f..d01813c25 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -48,7 +48,7 @@ defmodule Pleroma.Workers.ReceiverWorker do {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} {:error, :already_present} -> {:cancel, :already_present} {:error, {:validate_object, _} = reason} -> {:cancel, reason} - {:error, {:validate, _} = reason} -> {:cancel, reason} + {:error, {:error, {:validate, {:error, _changeset} = reason}}} -> {:cancel, reason} {:error, {:reject, _} = reason} -> {:cancel, reason} {:signature, false} -> {:cancel, :invalid_signature} {:error, "Object has been deleted"} = reason -> {:cancel, reason} -- cgit v1.2.3 From 3a79f060bb06ff862270eae67a59e98651f3f2d7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 15:11:38 -0400 Subject: Add missing type --- lib/pleroma/conversation/participation.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 4ed93e5bd..5d3344cc5 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Conversation.Participation do import Ecto.Changeset import Ecto.Query + @type t :: %__MODULE__{} + schema "conversation_participations" do belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:conversation, Conversation) -- cgit v1.2.3 From b1d33483310df3b5b5a4570ffc33f55b2eeca9b6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 15:12:34 -0400 Subject: Annotate public functions with typespecs and mark some functions as private --- lib/pleroma/web/common_api.ex | 48 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 36e7efd8d..411acf8fb 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -26,6 +26,7 @@ defmodule Pleroma.Web.CommonAPI do require Pleroma.Constants require Logger + @spec block(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} def block(blocker, blocked) do with {:ok, block_data, _} <- Builder.block(blocker, blocked), {:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do @@ -33,6 +34,8 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec post_chat_message(User.t(), User.t(), String.t(), list()) :: + {:ok, Activity.t()} | {:error, any()} def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]), :ok <- validate_chat_attachment_attribution(maybe_attachment, user), @@ -96,6 +99,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec unblock(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} def unblock(blocker, blocked) do with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)}, {:ok, unblock_data, _} <- Builder.undo(blocker, block), @@ -115,6 +119,8 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec follow(User.t(), User.t()) :: + {:ok, User.t(), User.t(), Activity.t() | Object.t()} | {:error, :rejected} def follow(follower, followed) do timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout]) @@ -129,6 +135,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec unfollow(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} def unfollow(follower, unfollowed) do with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed), {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed), @@ -138,6 +145,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec accept_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} def accept_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, accept_data, _} <- Builder.accept(followed, follow_activity), @@ -146,6 +154,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec reject_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} | nil def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, reject_data, _} <- Builder.reject(followed, follow_activity), @@ -154,6 +163,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec delete(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} def delete(activity_id, user) do with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(activity_id, filter: [])}, @@ -203,6 +213,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec repeat(String.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def repeat(id, user, params \\ %{}) do with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), object = %Object{} <- Object.normalize(activity, fetch: false), @@ -220,6 +231,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec unrepeat(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} def unrepeat(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(id)}, @@ -235,7 +247,7 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec favorite(User.t(), binary()) :: {:ok, Activity.t() | :already_liked} | {:error, any()} + @spec favorite(User.t(), String.t()) :: {:ok, Activity.t()} | {:error, any()} def favorite(%User{} = user, id) do case favorite_helper(user, id) do {:ok, _} = res -> @@ -250,7 +262,7 @@ defmodule Pleroma.Web.CommonAPI do end end - def favorite_helper(user, id) do + defp favorite_helper(user, id) do with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)}, {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)}, {_, {:ok, %Activity{} = activity, _meta}} <- @@ -273,6 +285,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec unfavorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} def unfavorite(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(id)}, @@ -288,6 +301,8 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec react_with_emoji(String.t(), User.t(), String.t()) :: + {:ok, Activity.t()} | {:error, any()} def react_with_emoji(id, user, emoji) do with %Activity{} = activity <- Activity.get_by_id(id), object <- Object.normalize(activity, fetch: false), @@ -300,6 +315,8 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec unreact_with_emoji(String.t(), User.t(), String.t()) :: + {:ok, Activity.t()} | {:error, any()} def unreact_with_emoji(id, user, emoji) do with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji), {_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(reaction_activity)}, @@ -312,6 +329,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec vote(User.t(), Object.t(), list()) :: {:ok, list(), Object.t()} | {:error, any()} def vote(user, %{data: %{"type" => "Question"}} = object, choices) do with :ok <- validate_not_author(object, user), :ok <- validate_existing_votes(user, object), @@ -373,14 +391,16 @@ defmodule Pleroma.Web.CommonAPI do end end - def public_announce?(_, %{visibility: visibility}) - when visibility in ~w{public unlisted private direct}, - do: visibility in ~w(public unlisted) + defp public_announce?(_, %{visibility: visibility}) + when visibility in ~w{public unlisted private direct}, + do: visibility in ~w(public unlisted) - def public_announce?(object, _) do + defp public_announce?(object, _) do Visibility.public?(object) end + @spec get_visibility(map(), map() | nil, Participation.t() | nil) :: + {String.t() | nil, String.t() | nil} def get_visibility(_, _, %Participation{}), do: {"direct", "direct"} def get_visibility(%{visibility: visibility}, in_reply_to, _) @@ -399,6 +419,7 @@ defmodule Pleroma.Web.CommonAPI do def get_visibility(_, in_reply_to, _), do: {"public", get_replied_to_visibility(in_reply_to)} + @spec get_replied_to_visibility(Activity.t() | nil) :: String.t() | nil def get_replied_to_visibility(nil), do: nil def get_replied_to_visibility(activity) do @@ -407,6 +428,8 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec check_expiry_date({:ok, nil | integer()} | String.t()) :: + {:ok, boolean() | nil} | {:error, String.t()} def check_expiry_date({:ok, nil} = res), do: res def check_expiry_date({:ok, in_seconds}) do @@ -424,18 +447,21 @@ defmodule Pleroma.Web.CommonAPI do |> check_expiry_date() end + @spec listen(User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def listen(user, data) do with {:ok, draft} <- ActivityDraft.listen(user, data) do ActivityPub.listen(draft.changes) end end + @spec post(User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def post(user, %{status: _} = data) do with {:ok, draft} <- ActivityDraft.create(user, data) do ActivityPub.create(draft.changes, draft.preview?) end end + @spec update(User.t(), Activity.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def update(user, orig_activity, changes) do with orig_object <- Object.normalize(orig_activity), {:ok, new_object} <- make_update_data(user, orig_object, changes), @@ -526,6 +552,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec add_mute(User.t(), Activity.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def add_mute(user, activity, params \\ %{}) do expires_in = Map.get(params, :expires_in, 0) @@ -545,6 +572,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec remove_mute(User.t(), Activity.t()) :: {:ok, Activity.t()} | {:error, any()} def remove_mute(%User{} = user, %Activity{} = activity) do ThreadMute.remove_mute(user.id, activity.data["context"]) {:ok, activity} @@ -564,6 +592,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec thread_muted?(User.t(), Activity.t()) :: boolean() def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}}) when is_binary(context) do ThreadMute.exists?(user_id, context) @@ -571,6 +600,7 @@ defmodule Pleroma.Web.CommonAPI do def thread_muted?(_, _), do: false + @spec report(User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def report(user, data) do with {:ok, account} <- get_reported_account(data.account_id), {:ok, {content_html, _, _}} <- make_report_content_html(data[:comment]), @@ -604,6 +634,8 @@ defmodule Pleroma.Web.CommonAPI do |> Enum.filter(&Rule.exists?/1) end + @spec update_report_state(String.t() | [String.t()], String.t()) :: + {:ok, any()} | {:error, any()} def update_report_state(activity_ids, state) when is_list(activity_ids) do case Utils.update_report_state(activity_ids, state) do :ok -> {:ok, activity_ids} @@ -619,6 +651,7 @@ defmodule Pleroma.Web.CommonAPI do end end + @spec update_activity_scope(String.t(), map()) :: {:ok, any()} | {:error, any()} def update_activity_scope(activity_id, opts \\ %{}) do with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), {:ok, activity} <- toggle_sensitive(activity, opts) do @@ -652,14 +685,17 @@ defmodule Pleroma.Web.CommonAPI do defp set_visibility(activity, _), do: {:ok, activity} + @spec hide_reblogs(User.t(), User.t()) :: {:ok, any()} | {:error, any()} def hide_reblogs(%User{} = user, %User{} = target) do UserRelationship.create_reblog_mute(user, target) end + @spec show_reblogs(User.t(), User.t()) :: {:ok, any()} | {:error, any()} def show_reblogs(%User{} = user, %User{} = target) do UserRelationship.delete_reblog_mute(user, target) end + @spec get_user(String.t(), boolean()) :: User.t() | nil def get_user(ap_id, fake_record_fallback \\ true) do cond do user = User.get_cached_by_ap_id(ap_id) -> -- cgit v1.2.3 From 7e37882cf7c8a12f13c9525fe70e286d2101af46 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 17:19:31 -0400 Subject: Fix order of args for favorite/2 --- lib/pleroma/web/common_api.ex | 4 ++-- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 411acf8fb..f8a03680a 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -247,8 +247,8 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec favorite(User.t(), String.t()) :: {:ok, Activity.t()} | {:error, any()} - def favorite(%User{} = user, id) do + @spec favorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} + def favorite(id, %User{} = user) do case favorite_helper(user, id) do {:ok, _} = res -> res diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 83e1bee54..80386dc45 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -357,7 +357,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do conn, _ ) do - with {:ok, _fav} <- CommonAPI.favorite(user, activity_id), + with {:ok, _fav} <- CommonAPI.favorite(activity_id, user), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end -- cgit v1.2.3 From f602813d314533a50ab7cadf64f1f0fbbb863cc6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 17:49:30 -0400 Subject: Fix order of args for update/2 --- lib/pleroma/web/common_api.ex | 4 ++-- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index f8a03680a..cfb36bc1c 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -461,8 +461,8 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec update(User.t(), Activity.t(), map()) :: {:ok, Activity.t()} | {:error, any()} - def update(user, orig_activity, changes) do + @spec update(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} + def update(orig_activity, %User{} = user, changes) do with orig_object <- Object.normalize(orig_activity), {:ok, new_object} <- make_update_data(user, orig_object, changes), {:ok, update_data, _} <- Builder.update(user, new_object), diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 80386dc45..f75efd00d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -276,7 +276,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do actor <- Activity.user_actor(activity), {_, true} <- {:own_status, actor.id == user.id}, changes <- body_params |> put_application(conn), - {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(user, activity, changes)}, + {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(activity, user, changes)}, {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do try_render(conn, "show.json", activity: activity, -- cgit v1.2.3 From d27ad36ce4b2f00d89ca96bd4d7c6f688ec67262 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 17:59:33 -0400 Subject: Fix order of args for remove_mute/2 --- lib/pleroma/web/common_api.ex | 9 +++++---- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 2 +- lib/pleroma/workers/mute_expire_worker.ex | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index cfb36bc1c..751260a46 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -572,16 +572,17 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec remove_mute(User.t(), Activity.t()) :: {:ok, Activity.t()} | {:error, any()} - def remove_mute(%User{} = user, %Activity{} = activity) do + @spec remove_mute(Activity.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} + def remove_mute(%Activity{} = activity, %User{} = user) do ThreadMute.remove_mute(user.id, activity.data["context"]) {:ok, activity} end - def remove_mute(user_id, activity_id) do + @spec remove_mute(String.t(), String.t()) :: {:ok, Activity.t()} | {:error, any()} + def remove_mute(activity_id, user_id) do with {:user, %User{} = user} <- {:user, User.get_by_id(user_id)}, {:activity, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)} do - remove_mute(user, activity) + remove_mute(activity, user) else {what, result} = error -> Logger.warning( diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index f75efd00d..ab5a104dd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -467,7 +467,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do _ ) do with %Activity{} = activity <- Activity.get_by_id(id), - {:ok, activity} <- CommonAPI.remove_mute(user, activity) do + {:ok, activity} <- CommonAPI.remove_mute(activity, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index 8ad287a7f..a7ab5883a 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Workers.MuteExpireWorker do def perform(%Job{ args: %{"op" => "unmute_conversation", "user_id" => user_id, "activity_id" => activity_id} }) do - Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id) + Pleroma.Web.CommonAPI.remove_mute(activity_id, user_id) :ok end -- cgit v1.2.3 From 4601473aaf65cfc5696e17069014a92750f1560d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 18:18:30 -0400 Subject: Fix order of args for add_mute/2 --- lib/pleroma/web/common_api.ex | 4 ++-- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 751260a46..b5ba081b8 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -552,8 +552,8 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec add_mute(User.t(), Activity.t(), map()) :: {:ok, Activity.t()} | {:error, any()} - def add_mute(user, activity, params \\ %{}) do + @spec add_mute(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()} + def add_mute(activity, user, params \\ %{}) do expires_in = Map.get(params, :expires_in, 0) with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]), diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index ab5a104dd..b9b236920 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -453,7 +453,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do _ ) do with %Activity{} = activity <- Activity.get_by_id(id), - {:ok, activity} <- CommonAPI.add_mute(user, activity, params) do + {:ok, activity} <- CommonAPI.add_mute(activity, user, params) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end -- cgit v1.2.3 From 8127e0d8cce9fa8fb62f6699fdee1bed466a6fb6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 18:23:13 -0400 Subject: Fix order of args for thread_muted?/2 --- lib/pleroma/notification.ex | 2 +- lib/pleroma/web/common_api.ex | 4 ++-- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- lib/pleroma/web/streamer.ex | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index de2508b93..75f4ba503 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -734,7 +734,7 @@ defmodule Pleroma.Notification do def mark_as_read?(activity, target_user) do user = Activity.user_actor(activity) - User.mutes_user?(target_user, user) || CommonAPI.thread_muted?(target_user, activity) + User.mutes_user?(target_user, user) || CommonAPI.thread_muted?(activity, target_user) end def for_user_and_activity(user, activity) do diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index b5ba081b8..f26cd8c1e 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -593,8 +593,8 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec thread_muted?(User.t(), Activity.t()) :: boolean() - def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}}) + @spec thread_muted?(Activity.t(), User.t()) :: boolean() + def thread_muted?(%{data: %{"context" => context}}, %User{id: user_id}) when is_binary(context) do ThreadMute.exists?(user_id, context) end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index d9d7e516a..747638c53 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -293,7 +293,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do cond do is_nil(opts[:for]) -> false is_boolean(activity.thread_muted?) -> activity.thread_muted? - true -> CommonAPI.thread_muted?(opts[:for], activity) + true -> CommonAPI.thread_muted?(activity, opts[:for]) end attachment_data = object.data["attachment"] || [] diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 9abdfae30..76dc0f42d 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -206,7 +206,7 @@ defmodule Pleroma.Web.Streamer do false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host), false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, parent_host), true <- thread_containment(item, user), - false <- CommonAPI.thread_muted?(user, parent) do + false <- CommonAPI.thread_muted?(parent, user) do false else _ -> true -- cgit v1.2.3 From 1cccc0fc2193b7ba0a87bf0902abce429748153e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 18:38:02 -0400 Subject: Fix order of args for vote/3 --- lib/pleroma/web/common_api.ex | 4 ++-- lib/pleroma/web/mastodon_api/controllers/poll_controller.ex | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index f26cd8c1e..2eb2bcc82 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -329,8 +329,8 @@ defmodule Pleroma.Web.CommonAPI do end end - @spec vote(User.t(), Object.t(), list()) :: {:ok, list(), Object.t()} | {:error, any()} - def vote(user, %{data: %{"type" => "Question"}} = object, choices) do + @spec vote(Object.t(), User.t(), list()) :: {:ok, list(), Object.t()} | {:error, any()} + def vote(%Object{data: %{"type" => "Question"}} = object, %User{} = user, choices) do with :ok <- validate_not_author(object, user), :ok <- validate_existing_votes(user, object), {:ok, options, choices} <- normalize_and_validate_choices(choices, object) do diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index b074ee405..a2af8148c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -51,7 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do with %Object{data: %{"type" => "Question"}} = 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), - {:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do + {:ok, _activities, object} <- get_cached_vote_or_vote(object, user, choices) do try_render(conn, "show.json", %{object: object, for: user}) else nil -> render_error(conn, :not_found, "Record not found") @@ -60,11 +60,11 @@ defmodule Pleroma.Web.MastodonAPI.PollController do end end - defp get_cached_vote_or_vote(user, object, choices) do + defp get_cached_vote_or_vote(object, user, choices) do idempotency_key = "polls:#{user.id}:#{object.data["id"]}" @cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> - case CommonAPI.vote(user, object, choices) do + case CommonAPI.vote(object, user, choices) do {:error, _message} = res -> {:ignore, res} res -> {:commit, res} end -- cgit v1.2.3 From cbc5e4841725a19761f5d869e2cc8f5152692791 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 18:39:20 -0400 Subject: Fix order of args for block/2 --- lib/pleroma/user/import.ex | 2 +- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex index 4baa7e3a4..f62f1203d 100644 --- a/lib/pleroma/user/import.ex +++ b/lib/pleroma/user/import.ex @@ -31,7 +31,7 @@ defmodule Pleroma.User.Import do identifiers, fn identifier -> with {:ok, %User{} = blocked} <- User.get_or_fetch(identifier), - {:ok, _block} <- CommonAPI.block(blocker, blocked) do + {:ok, _block} <- CommonAPI.block(blocked, blocker) do blocked else error -> handle_error(:blocks_import, identifier, error) diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 2eb2bcc82..4109f8992 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -27,7 +27,7 @@ defmodule Pleroma.Web.CommonAPI do require Logger @spec block(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} - def block(blocker, blocked) do + def block(blocked, blocker) do with {:ok, block_data, _} <- Builder.block(blocker, blocked), {:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do {:ok, block} diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 47e6f0a64..57439c6d4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -495,7 +495,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/block" def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do + with {:ok, _activity} <- CommonAPI.block(blocked, blocker) do render(conn, "relationship.json", user: blocker, target: blocked) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) -- cgit v1.2.3 From 082319ff482f82c72b633e88dbe06c06205f4faf Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 18:44:32 -0400 Subject: Fix order of args for unblock/2 --- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 4109f8992..9fafa1f27 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -100,7 +100,7 @@ defmodule Pleroma.Web.CommonAPI do end @spec unblock(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()} - def unblock(blocker, blocked) do + def unblock(blocked, blocker) do with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)}, {:ok, unblock_data, _} <- Builder.undo(blocker, block), {:ok, unblock, _} <- Pipeline.common_pipeline(unblock_data, local: true) do diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 57439c6d4..00dc2e27c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -504,7 +504,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/unblock" def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, _activity} <- CommonAPI.unblock(blocker, blocked) do + with {:ok, _activity} <- CommonAPI.unblock(blocked, blocker) do render(conn, "relationship.json", user: blocker, target: blocked) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) -- cgit v1.2.3 From f79a16c062a14634d8b5f90e08df0b22b799bb45 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 19:07:55 -0400 Subject: Fix order of args for follow/2 --- lib/pleroma/following_relationship.ex | 2 +- lib/pleroma/user/import.ex | 2 +- lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex | 2 +- lib/pleroma/web/activity_pub/relay.ex | 2 +- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/mastodon_api.ex | 2 +- lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index f38c2fce9..3ec05da13 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -199,7 +199,7 @@ defmodule Pleroma.FollowingRelationship do |> preload([:follower]) |> Repo.all() |> Enum.map(fn following_relationship -> - Pleroma.Web.CommonAPI.follow(following_relationship.follower, target) + Pleroma.Web.CommonAPI.follow(target, following_relationship.follower) Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) end) |> case do diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex index f62f1203d..53ffd1ab3 100644 --- a/lib/pleroma/user/import.ex +++ b/lib/pleroma/user/import.ex @@ -46,7 +46,7 @@ defmodule Pleroma.User.Import do fn identifier -> with {:ok, %User{} = followed} <- User.get_or_fetch(identifier), {:ok, follower, followed} <- User.maybe_direct_follow(follower, followed), - {:ok, _, _, _} <- CommonAPI.follow(follower, followed) do + {:ok, _, _, _} <- CommonAPI.follow(followed, follower) do followed else error -> handle_error(:follow_import, identifier, error) diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex index 5a4a97626..55ea2683c 100644 --- a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex @@ -49,7 +49,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do "#{__MODULE__}: Follow request from #{follower.nickname} to #{user.nickname}" ) - CommonAPI.follow(follower, user) + CommonAPI.follow(user, follower) end end) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 91a647f29..cff234940 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do def follow(target_instance) do with %User{} = local_user <- get_actor(), {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), - {:ok, _, _, activity} <- CommonAPI.follow(local_user, target_user) do + {:ok, _, _, activity} <- CommonAPI.follow(target_user, local_user) do Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}") {:ok, activity} else diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 9fafa1f27..08bca4ffc 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -121,7 +121,7 @@ defmodule Pleroma.Web.CommonAPI do @spec follow(User.t(), User.t()) :: {:ok, User.t(), User.t(), Activity.t() | Object.t()} | {:error, :rejected} - def follow(follower, followed) do + def follow(followed, follower) do timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout]) with {:ok, follow_data, _} <- Builder.follow(follower, followed), diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 467dc2fac..a382e2826 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -16,7 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do def follow(follower, followed, params \\ %{}) do result = if not User.following?(follower, followed) do - CommonAPI.follow(follower, followed) + CommonAPI.follow(followed, follower) else {:ok, follower, followed, nil} end diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index 1557d95ab..38ebc8c5d 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -73,7 +73,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do # def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, - {:ok, _, _, _} <- CommonAPI.follow(user, followee) do + {:ok, _, _, _} <- CommonAPI.follow(followee, user) do redirect(conn, to: "/users/#{followee.id}") else error -> @@ -90,7 +90,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, {_, {:ok, user}, _} <- {:auth, WrapperAuthenticator.get_user(conn), followee}, {_, _, _, false} <- {:mfa_required, followee, user, MFA.require?(user)}, - {:ok, _, _, _} <- CommonAPI.follow(user, followee) do + {:ok, _, _, _} <- CommonAPI.follow(followee, user) do redirect(conn, to: "/users/#{followee.id}") else error -> @@ -108,7 +108,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do {_, _, {:ok, %{user: user}}} <- {:mfa_token, followee, MFA.Token.validate(token)}, {_, _, _, {:ok, _}} <- {:verify_mfa_code, followee, token, TOTPAuthenticator.verify(code, user)}, - {:ok, _, _, _} <- CommonAPI.follow(user, followee) do + {:ok, _, _, _} <- CommonAPI.follow(followee, user) do redirect(conn, to: "/users/#{followee.id}") else error -> -- cgit v1.2.3 From adb93f7e5d9b86a02f5114f0597b8274e4ada88d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 19:11:22 -0400 Subject: Fix order of args for unfollow/2 --- lib/pleroma/following_relationship.ex | 2 +- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 3ec05da13..495488dfd 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -200,7 +200,7 @@ defmodule Pleroma.FollowingRelationship do |> Repo.all() |> Enum.map(fn following_relationship -> Pleroma.Web.CommonAPI.follow(target, following_relationship.follower) - Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) + Pleroma.Web.CommonAPI.unfollow(origin, following_relationship.follower) end) |> case do [] -> diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 08bca4ffc..827710c11 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -136,7 +136,7 @@ defmodule Pleroma.Web.CommonAPI do end @spec unfollow(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} - def unfollow(follower, unfollowed) do + def unfollow(unfollowed, follower) do with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed), {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed), {:ok, _subscription} <- User.unsubscribe(follower, unfollowed), diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 00dc2e27c..80ab95a57 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -460,7 +460,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) do - with {:ok, follower} <- CommonAPI.unfollow(follower, followed) do + with {:ok, follower} <- CommonAPI.unfollow(followed, follower) do render(conn, "relationship.json", user: follower, target: followed) end end -- cgit v1.2.3 From 3f4f567c9cbc0182eb99ec951a6088bfc5bb57d5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 19:16:00 -0400 Subject: Fix order of args for hide_reblogs/2 --- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/mastodon_api.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 827710c11..13d738310 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -687,7 +687,7 @@ defmodule Pleroma.Web.CommonAPI do defp set_visibility(activity, _), do: {:ok, activity} @spec hide_reblogs(User.t(), User.t()) :: {:ok, any()} | {:error, any()} - def hide_reblogs(%User{} = user, %User{} = target) do + def hide_reblogs(%User{} = target, %User{} = user) do UserRelationship.create_reblog_mute(user, target) end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index a382e2826..c6243d654 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do end defp set_reblogs_visibility(false, {:ok, follower, followed, _}) do - CommonAPI.hide_reblogs(follower, followed) + CommonAPI.hide_reblogs(followed, follower) end defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do -- cgit v1.2.3 From 12f498bc0d996ccdacf7769d57b96b1c2ecabdb3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jul 2024 19:19:21 -0400 Subject: Fix order of args for show_reblogs/2 --- lib/pleroma/web/common_api.ex | 2 +- lib/pleroma/web/mastodon_api/mastodon_api.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 13d738310..06faf845e 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -692,7 +692,7 @@ defmodule Pleroma.Web.CommonAPI do end @spec show_reblogs(User.t(), User.t()) :: {:ok, any()} | {:error, any()} - def show_reblogs(%User{} = user, %User{} = target) do + def show_reblogs(%User{} = target, %User{} = user) do UserRelationship.delete_reblog_mute(user, target) end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index c6243d654..6dcbfb097 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -34,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do end defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do - CommonAPI.show_reblogs(follower, followed) + CommonAPI.show_reblogs(followed, follower) end defp set_subscription(true, {:ok, follower, followed, _}) do -- cgit v1.2.3 From 2ee8f4f0629b950349409d69395015369c45b633 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 23 Jul 2024 09:43:41 -0400 Subject: Fix dialyzer error lib/pleroma/application.ex:1:pattern_match The pattern can never match the type true. --- lib/pleroma/application.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d98293e8e..cb15dc1e9 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -14,7 +14,6 @@ defmodule Pleroma.Application do @name Mix.Project.config()[:name] @version Mix.Project.config()[:version] @repository Mix.Project.config()[:source_url] - @compile_env Mix.env() def name, do: @name def version, do: @version @@ -53,7 +52,7 @@ defmodule Pleroma.Application do Pleroma.Config.Oban.warn() Config.DeprecationWarnings.warn() - if @compile_env != :test do + if Config.get([Pleroma.Web.Plugs.HTTPSecurityPlug, :enable], true) do Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() end -- cgit v1.2.3 From 858fd01c012a48928b55999e8209371a5049c3e6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 24 Jul 2024 15:40:15 -0400 Subject: Pleroma.HTTP: permit passing through custom Tesla Middlware for requests --- lib/pleroma/http.ex | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex index ec837e509..c11317850 100644 --- a/lib/pleroma/http.ex +++ b/lib/pleroma/http.ex @@ -68,7 +68,9 @@ defmodule Pleroma.HTTP do adapter = Application.get_env(:tesla, :adapter) - client = Tesla.client(adapter_middlewares(adapter), adapter) + extra_middleware = options[:tesla_middleware] || [] + + client = Tesla.client(adapter_middlewares(adapter, extra_middleware), adapter) maybe_limit( fn -> @@ -102,20 +104,21 @@ defmodule Pleroma.HTTP do fun.() end - defp adapter_middlewares(Tesla.Adapter.Gun) do - [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool] + defp adapter_middlewares(Tesla.Adapter.Gun, extra_middleware) do + [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool] ++ + extra_middleware end - defp adapter_middlewares({Tesla.Adapter.Finch, _}) do - [Tesla.Middleware.FollowRedirects] + defp adapter_middlewares({Tesla.Adapter.Finch, _}, extra_middleware) do + [Tesla.Middleware.FollowRedirects] ++ extra_middleware end - defp adapter_middlewares(_) do + defp adapter_middlewares(_, extra_middleware) do if Pleroma.Config.get(:env) == :test do # Emulate redirects in test env, which are handled by adapters in other environments [Tesla.Middleware.FollowRedirects] else - [] + extra_middleware end end end -- cgit v1.2.3 From 731f7b87d24bd58d5349c7d1f564cd73dbf59aa9 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 24 Jul 2024 15:42:50 -0400 Subject: Pad RichMediaWorker timeout to be 2s longer than the Rich Media HTTP timeout --- lib/pleroma/web/rich_media/helpers.ex | 5 ++++- lib/pleroma/workers/rich_media_worker.ex | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index fba23c657..e2889b351 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -69,9 +69,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do end defp http_options do + timeout = Config.get!([:rich_media, :timeout]) + [ pool: :rich_media, - max_body: Config.get([:rich_media, :max_body], 5_000_000) + max_body: Config.get([:rich_media, :max_body], 5_000_000), + tesla_middleware: [{Tesla.Middleware.Timeout, timeout: timeout}] ] end end diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index 30f9d9e9e..0a1c6d58c 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.RichMediaWorker do + alias Pleroma.Config alias Pleroma.Web.RichMedia.Backfill alias Pleroma.Web.RichMedia.Card @@ -31,6 +32,13 @@ defmodule Pleroma.Workers.RichMediaWorker do end end + # There is timeout value enforced by Tesla.Middleware.Timeout + # which can be found in the RichMedia.Helpers module to allow us to detect + # a slow/infinite data stream and insert a negative cache entry for the URL + # We pad it by 2 seconds to be certain a slow connection is detected and we + # can inject a negative cache entry for the URL @impl Oban.Worker - def timeout(_job), do: :timer.seconds(5) + def timeout(_job) do + Config.get!([:rich_media, :timeout]) + :timer.seconds(2) + end end -- cgit v1.2.3 From 5a62868106465bd30be11922b4ff3b11b3c174aa Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 24 Jul 2024 15:43:45 -0400 Subject: Consider errors during HTTP GET and HEAD to be unrecoverable and insert a negative cache entry This is for a normal HTTP error response or timeout while receiving the data. A hard error from a process crash, DNS lookup failure, etc should produce a different response than {:ok, %Tesla.Env{}} and the request/job will be retryable. --- lib/pleroma/web/rich_media/backfill.ex | 6 +----- lib/pleroma/workers/rich_media_worker.ex | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index a66422e71..cf638fa29 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -36,13 +36,9 @@ defmodule Pleroma.Web.RichMedia.Backfill do :ok {:error, type} = error - when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> + when type in [:invalid_metadata, :body_too_large, :content_type, :validate, :get, :head] -> negative_cache(url_hash) error - - {:error, type} = error - when type in [:get, :head] -> - error end end diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex index 0a1c6d58c..2ebf42d4f 100644 --- a/lib/pleroma/workers/rich_media_worker.ex +++ b/lib/pleroma/workers/rich_media_worker.ex @@ -20,13 +20,9 @@ defmodule Pleroma.Workers.RichMediaWorker do :ok {:error, type} - when type in [:invalid_metadata, :body_too_large, :content_type, :validate] -> + when type in [:invalid_metadata, :body_too_large, :content_type, :validate, :get, :head] -> {:cancel, type} - {:error, type} - when type in [:get, :head] -> - {:error, type} - error -> {:error, error} end -- cgit v1.2.3 From 97d488aea3ce75e52d4e6ba9a3b5e4447b535879 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 24 Jul 2024 15:45:35 -0400 Subject: Fix RichMedia negative cache entries The negative cache entry was a nil value, but that is an expected response when the cache is missing an entry so it didn't work as intended. --- lib/pleroma/web/rich_media/backfill.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/rich_media/backfill.ex b/lib/pleroma/web/rich_media/backfill.ex index cf638fa29..1cd90629f 100644 --- a/lib/pleroma/web/rich_media/backfill.ex +++ b/lib/pleroma/web/rich_media/backfill.ex @@ -64,5 +64,5 @@ defmodule Pleroma.Web.RichMedia.Backfill do defp warm_cache(key, val), do: @cachex.put(:rich_media_cache, key, val) defp negative_cache(key, ttl \\ :timer.minutes(15)), - do: @cachex.put(:rich_media_cache, key, nil, ttl: ttl) + do: @cachex.put(:rich_media_cache, key, :error, ttl: ttl) end -- cgit v1.2.3 From 1a482a73c3b99f7fdc512b734dd746e9f9cd396d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Jul 2024 11:18:27 -0400 Subject: Fix Optimistic Inbox for failed signatures When signatures fail on incoming activities we put the job into Oban to be processed later instead of doing the user fetching and validation inline which is expensive and increases latency on the incoming POST request. Unfortunately we did not retain the :method, :request_path, and :query_string parameters from the conn so the signature validation and Oban Job would always fail. This was most obvious when Mastodon sends Deletes for users your server has never seen before. --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 11 +++++++++-- lib/pleroma/workers/receiver_worker.ex | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index e6161455d..cdd054e1a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -293,8 +293,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end - def inbox(%{assigns: %{valid_signature: false}, req_headers: req_headers} = conn, params) do - Federator.incoming_ap_doc(%{req_headers: req_headers, params: params}) + def inbox(%{assigns: %{valid_signature: false}} = conn, params) do + Federator.incoming_ap_doc(%{ + method: conn.method, + req_headers: conn.req_headers, + request_path: conn.request_path, + params: params, + query_string: conn.query_string + }) + json(conn, "ok") end diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index d01813c25..1a2881cd1 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -12,13 +12,26 @@ defmodule Pleroma.Workers.ReceiverWorker do @impl Oban.Worker def perform(%Job{ - args: %{"op" => "incoming_ap_doc", "req_headers" => req_headers, "params" => params} + args: %{ + "op" => "incoming_ap_doc", + "method" => method, + "params" => params, + "req_headers" => req_headers, + "request_path" => request_path, + "query_string" => query_string + } }) do # Oban's serialization converts our tuple headers to lists. # Revert it for the signature validation. req_headers = Enum.into(req_headers, [], &List.to_tuple(&1)) - conn_data = %{params: params, req_headers: req_headers} + conn_data = %{ + method: method, + params: params, + req_headers: req_headers, + request_path: request_path, + query_string: query_string + } with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]), {:ok, _public_key} <- Signature.refetch_public_key(conn_data), -- cgit v1.2.3 From 1b9c887dbb8d87814f8d9cc11cfcbc8802348b22 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Jul 2024 12:54:27 -0400 Subject: Extract validate_signature/2 from the HTTPSignaturePlug This logic only exists in the Plug, so attempting to validate the signature by calling the library function HTTPSignature.validate_conn/2 directly will never work because we do not attempt to construct the (request-target) and @request-target headers with both the commonly misinterpreted and correct implementation of this field. Therefore all attempts to validate a signature from an Oban Job will fail. --- lib/pleroma/signature.ex | 52 ++++++++++++++++++++++++++++ lib/pleroma/web/plugs/http_signature_plug.ex | 50 ++------------------------ lib/pleroma/workers/receiver_worker.ex | 2 +- 3 files changed, 55 insertions(+), 49 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 900d40c4b..51dac2402 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -10,6 +10,14 @@ defmodule Pleroma.Signature do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + import Plug.Conn, only: [put_req_header: 3] + + @http_signatures_impl Application.compile_env( + :pleroma, + [__MODULE__, :http_signatures_impl], + HTTPSignatures + ) + @known_suffixes ["/publickey", "/main-key"] def key_id_to_actor_id(key_id) do @@ -85,4 +93,48 @@ defmodule Pleroma.Signature do def signed_date(%NaiveDateTime{} = date) do Timex.format!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") end + + @spec validate_signature(map(), String.t()) :: boolean() + def validate_signature(conn, request_target) do + # Newer drafts for HTTP signatures now use @request-target instead of the + # old (request-target). We'll now support both for incoming signatures. + conn = + conn + |> put_req_header("(request-target)", request_target) + |> put_req_header("@request-target", request_target) + + @http_signatures_impl.validate_conn(conn) + end + + @spec validate_signature(map()) :: boolean() + def validate_signature(conn) do + # This (request-target) is non-standard, but many implementations do it + # this way due to a misinterpretation of + # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06 + # "path" was interpreted as not having the query, though later examples + # show that it must be the absolute path + query. This behavior is kept to + # make sure most software (Pleroma itself, Mastodon, and probably others) + # do not break. + request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" + + # This is the proper way to build the @request-target, as expected by + # many HTTP signature libraries, clarified in the following draft: + # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6 + # It is the same as before, but containing the query part as well. + proper_target = request_target <> "?#{conn.query_string}" + + cond do + # Normal, non-standard behavior but expected by Pleroma and more. + validate_signature(conn, request_target) -> + true + + # Has query string and the previous one failed: let's try the standard. + conn.query_string != "" -> + validate_signature(conn, proper_target) + + # If there's no query string and signature fails, it's rotten. + true -> + false + end + end end diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index 6bf2dd432..67974599a 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -8,16 +8,12 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do import Plug.Conn import Phoenix.Controller, only: [get_format: 1, text: 2] + alias Pleroma.Signature alias Pleroma.Web.ActivityPub.MRF require Logger @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) - @http_signatures_impl Application.compile_env( - :pleroma, - [__MODULE__, :http_signatures_impl], - HTTPSignatures - ) def init(options) do options @@ -39,48 +35,6 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end end - defp validate_signature(conn, request_target) do - # Newer drafts for HTTP signatures now use @request-target instead of the - # old (request-target). We'll now support both for incoming signatures. - conn = - conn - |> put_req_header("(request-target)", request_target) - |> put_req_header("@request-target", request_target) - - @http_signatures_impl.validate_conn(conn) - end - - defp validate_signature(conn) do - # This (request-target) is non-standard, but many implementations do it - # this way due to a misinterpretation of - # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06 - # "path" was interpreted as not having the query, though later examples - # show that it must be the absolute path + query. This behavior is kept to - # make sure most software (Pleroma itself, Mastodon, and probably others) - # do not break. - request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" - - # This is the proper way to build the @request-target, as expected by - # many HTTP signature libraries, clarified in the following draft: - # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6 - # It is the same as before, but containing the query part as well. - proper_target = request_target <> "?#{conn.query_string}" - - cond do - # Normal, non-standard behavior but expected by Pleroma and more. - validate_signature(conn, request_target) -> - true - - # Has query string and the previous one failed: let's try the standard. - conn.query_string != "" -> - validate_signature(conn, proper_target) - - # If there's no query string and signature fails, it's rotten. - true -> - false - end - end - defp maybe_assign_valid_signature(conn) do if has_signature_header?(conn) do # we replace the digest header with the one we computed in DigestPlug @@ -90,7 +44,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do conn -> conn end - assign(conn, :valid_signature, validate_signature(conn)) + assign(conn, :valid_signature, Signature.validate_signature(conn)) else Logger.debug("No signature header!") conn diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 1a2881cd1..94624579e 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -35,7 +35,7 @@ defmodule Pleroma.Workers.ReceiverWorker do with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]), {:ok, _public_key} <- Signature.refetch_public_key(conn_data), - {:signature, true} <- {:signature, HTTPSignatures.validate_conn(conn_data)}, + {:signature, true} <- {:signature, Signature.validate_signature(conn_data)}, {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do {:ok, res} else -- cgit v1.2.3 From a964368e31ddf8a22dd36bbf2aae51f7d91bbdaf Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Jul 2024 14:33:51 -0400 Subject: Add test to fetch and validate an activity that originally failed signature --- lib/pleroma/web/federator.ex | 6 ++++-- lib/pleroma/workers/receiver_worker.ex | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 4b30fd21d..3d3101d61 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -35,10 +35,12 @@ defmodule Pleroma.Web.Federator do end # Client API - def incoming_ap_doc(%{params: params, req_headers: req_headers}) do + def incoming_ap_doc(%{params: _params, req_headers: _req_headers} = args) do + job_args = Enum.into(args, %{}, fn {k, v} -> {Atom.to_string(k), v} end) + ReceiverWorker.enqueue( "incoming_ap_doc", - %{"req_headers" => req_headers, "params" => params, "timeout" => :timer.seconds(20)}, + Map.put(job_args, "timeout", :timer.seconds(20)), priority: 2 ) end diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 94624579e..fd5c13fca 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Workers.ReceiverWorker do # Revert it for the signature validation. req_headers = Enum.into(req_headers, [], &List.to_tuple(&1)) - conn_data = %{ + conn_data = %Plug.Conn{ method: method, params: params, req_headers: req_headers, -- cgit v1.2.3 From 84b15ac1119396eeb9827fc5242489a4f5cb820b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Jul 2024 16:18:31 -0400 Subject: Improve specs and matching --- lib/pleroma/signature.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 51dac2402..0f3362ebe 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -94,8 +94,8 @@ defmodule Pleroma.Signature do Timex.format!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") end - @spec validate_signature(map(), String.t()) :: boolean() - def validate_signature(conn, request_target) do + @spec validate_signature(Plug.Conn.t(), String.t()) :: boolean() + def validate_signature(%Plug.Conn{} = conn, request_target) do # Newer drafts for HTTP signatures now use @request-target instead of the # old (request-target). We'll now support both for incoming signatures. conn = @@ -106,8 +106,8 @@ defmodule Pleroma.Signature do @http_signatures_impl.validate_conn(conn) end - @spec validate_signature(map()) :: boolean() - def validate_signature(conn) do + @spec validate_signature(Plug.Conn.t()) :: boolean() + def validate_signature(%Plug.Conn{} = conn) do # This (request-target) is non-standard, but many implementations do it # this way due to a misinterpretation of # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06 -- cgit v1.2.3 From c19d55cabb4932b9786fa8a4571d7b92e3925e00 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Jul 2024 16:18:45 -0400 Subject: Safer string concatenation --- lib/pleroma/signature.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 0f3362ebe..195513478 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -115,13 +115,13 @@ defmodule Pleroma.Signature do # show that it must be the absolute path + query. This behavior is kept to # make sure most software (Pleroma itself, Mastodon, and probably others) # do not break. - request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" + request_target = Enum.join([String.downcase(conn.method), conn.request_path], " ") # This is the proper way to build the @request-target, as expected by # many HTTP signature libraries, clarified in the following draft: # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6 # It is the same as before, but containing the query part as well. - proper_target = request_target <> "?#{conn.query_string}" + proper_target = Enum.join([request_target, "?", conn.query_string], "") cond do # Normal, non-standard behavior but expected by Pleroma and more. -- cgit v1.2.3