From 949a53e327fa2d4ca2099cd4ca6fa2e3fd9e789a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 5 Dec 2021 17:46:56 -0500 Subject: Log Ecto queries > 500ms --- lib/pleroma/telemetry/logger.ex | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 44d2f48dc..1dea13acd 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -12,10 +12,16 @@ defmodule Pleroma.Telemetry.Logger do [:pleroma, :connection_pool, :reclaim, :stop], [:pleroma, :connection_pool, :provision_failure], [:pleroma, :connection_pool, :client, :dead], - [:pleroma, :connection_pool, :client, :add] + [:pleroma, :connection_pool, :client, :add], + [:pleroma, :repo, :query] ] def attach do - :telemetry.attach_many("pleroma-logger", @events, &handle_event/4, []) + :telemetry.attach_many( + "pleroma-logger", + @events, + &Pleroma.Telemetry.Logger.handle_event/4, + [] + ) end # Passing anonymous functions instead of strings to logger is intentional, @@ -91,4 +97,39 @@ defmodule Pleroma.Telemetry.Logger do end def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok + + def handle_event( + [:pleroma, :repo, :query] = _name, + %{query_time: query_time} = _measurements, + %{source: source, query: query} = _metadata, + _config + ) + when query_time > 500_000 and source not in [nil, "oban_jobs"] do + {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace) + + stacktrace = + Enum.filter(stacktrace, fn + {__MODULE__, _, _, _} -> + false + + {mod, _, _, _} -> + mod + |> to_string() + |> String.starts_with?("Elixir.Pleroma.") + end) + + Logger.warn(fn -> + """ + Query took longer than 500ms! + + Total time: #{query_time / 1_000}ms + + #{inspect(query)} + + #{inspect(stacktrace, pretty: true)} + """ + end) + end + + def handle_event([:pleroma, :repo, :query], _measurements, _metadata, _config), do: :ok end -- cgit v1.2.3 From e009950845c6d1e7864bb68ea1258c58438ee3aa Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 19 Dec 2021 20:35:00 +0300 Subject: Slow queries logging improvements: added EXPLAIN results, listed params, improved stacktrace. --- lib/pleroma/telemetry/logger.ex | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 1dea13acd..c079f34f2 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -101,13 +101,19 @@ defmodule Pleroma.Telemetry.Logger do def handle_event( [:pleroma, :repo, :query] = _name, %{query_time: query_time} = _measurements, - %{source: source, query: query} = _metadata, + %{source: source, query: query, params: query_params, repo: repo} = _metadata, _config ) when query_time > 500_000 and source not in [nil, "oban_jobs"] do {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace) - stacktrace = + sql_explain = + with {:ok, %{rows: explain_result_rows}} <- + repo.query("EXPLAIN " <> query, query_params, log: false) do + Enum.map_join(explain_result_rows, "\n", & &1) + end + + pleroma_stacktrace = Enum.filter(stacktrace, fn {__MODULE__, _, _, _} -> false @@ -120,13 +126,17 @@ defmodule Pleroma.Telemetry.Logger do Logger.warn(fn -> """ - Query took longer than 500ms! + Slow query! Total time: #{query_time / 1_000}ms - #{inspect(query)} + #{query} + + #{inspect(query_params)} + + #{sql_explain} - #{inspect(stacktrace, pretty: true)} + #{Exception.format_stacktrace(pleroma_stacktrace)} """ end) end -- cgit v1.2.3 From 3e9e7178bc90754ad6f5414417079f6484b421e9 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 26 Dec 2021 22:49:00 +0300 Subject: Configurability of slow queries logging ([:pleroma, :telemetry, :slow_queries_logging]). Adjusted log messages truncation to 65 kb (was default: 8 kb). Non-truncated logging of slow query params. --- lib/pleroma/telemetry/logger.ex | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index c079f34f2..0f73ecc02 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -100,19 +100,34 @@ defmodule Pleroma.Telemetry.Logger do def handle_event( [:pleroma, :repo, :query] = _name, - %{query_time: query_time} = _measurements, - %{source: source, query: query, params: query_params, repo: repo} = _metadata, - _config - ) - when query_time > 500_000 and source not in [nil, "oban_jobs"] do - {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace) + %{query_time: query_time} = measurements, + %{source: source} = metadata, + config + ) do + logging_config = Pleroma.Config.get([:telemetry, :slow_queries_logging], []) + + if logging_config[:min_duration] && query_time > logging_config[:min_duration] and + (is_nil(logging_config[:exclude_sources]) or + source not in logging_config[:exclude_sources]) do + log_slow_query(measurements, metadata, config) + else + :ok + end + end + defp log_slow_query( + %{query_time: query_time} = _measurements, + %{source: _source, query: query, params: query_params, repo: repo} = _metadata, + _config + ) do sql_explain = with {:ok, %{rows: explain_result_rows}} <- repo.query("EXPLAIN " <> query, query_params, log: false) do Enum.map_join(explain_result_rows, "\n", & &1) end + {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace) + pleroma_stacktrace = Enum.filter(stacktrace, fn {__MODULE__, _, _, _} -> @@ -128,11 +143,11 @@ defmodule Pleroma.Telemetry.Logger do """ Slow query! - Total time: #{query_time / 1_000}ms + Total time: #{round(query_time / 1_000)} ms #{query} - #{inspect(query_params)} + #{inspect(query_params, limit: :infinity)} #{sql_explain} @@ -140,6 +155,4 @@ defmodule Pleroma.Telemetry.Logger do """ end) end - - def handle_event([:pleroma, :repo, :query], _measurements, _metadata, _config), do: :ok end -- cgit v1.2.3 From cd1041c3a413b9b3ba4c763308b5fd77a53d7c3c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:27:48 +0300 Subject: API: optionally restrict moderators from accessing sensitive data --- lib/pleroma/web/plugs/ensure_staff_privileged.ex | 31 ++++++++++++++++++++++++ lib/pleroma/web/router.ex | 31 +++++++++++++++++------- 2 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 lib/pleroma/web/plugs/ensure_staff_privileged.ex (limited to 'lib') diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged.ex b/lib/pleroma/web/plugs/ensure_staff_privileged.ex new file mode 100644 index 000000000..b15ddfc56 --- /dev/null +++ b/lib/pleroma/web/plugs/ensure_staff_privileged.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do + @moduledoc """ + Ensures if staff are privileged enough to do certain tasks + """ + + import Pleroma.Web.TranslationHelpers + import Plug.Conn + + alias Pleroma.User + alias Pleroma.Config + + def init(options) do + options + end + + def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn + + def call(conn, _) do + if Config.get!([:instance, :privileged_staff]) do + conn + else + conn + |> render_error(:forbidden, "User is not an admin.") + |> halt() + end + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index b2ca09784..7ba72994b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -101,6 +101,10 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.IdempotencyPlug) end + pipeline :require_privileged_staff do + plug(Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug) + end + pipeline :require_admin do plug(Pleroma.Web.Plugs.UserIsAdminPlug) end @@ -228,6 +232,24 @@ defmodule Pleroma.Web.Router do post("/backups", AdminAPIController, :create_backup) end + # AdminAPI: admins and mods (staff) can perform these actions (if enabled by config) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through([:admin_api, :require_privileged_staff]) + + delete("/users", UserController, :delete) + + get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) + patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) + + get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) + get("/users/:nickname/chats", AdminAPIController, :list_user_chats) + + get("/statuses", StatusController, :index) + + get("/chats/:id", ChatController, :show) + get("/chats/:id/messages", ChatController, :messages) + end + # AdminAPI: admins and mods (staff) can perform these actions scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through(:admin_api) @@ -240,22 +262,16 @@ defmodule Pleroma.Web.Router do patch("/users/deactivate", UserController, :deactivate) patch("/users/approve", UserController, :approve) - delete("/users", UserController, :delete) - post("/users/invite_token", InviteController, :create) get("/users/invites", InviteController, :index) post("/users/revoke_invite", InviteController, :revoke) post("/users/email_invite", InviteController, :email) - get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) patch("/users/force_password_reset", AdminAPIController, :force_password_reset) get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) - patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) get("/users", UserController, :index) get("/users/:nickname", UserController, :show) - get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) - get("/users/:nickname/chats", AdminAPIController, :list_user_chats) get("/instances/:instance/statuses", InstanceController, :list_statuses) delete("/instances/:instance", InstanceController, :delete) @@ -269,15 +285,12 @@ defmodule Pleroma.Web.Router do get("/statuses/:id", StatusController, :show) put("/statuses/:id", StatusController, :update) delete("/statuses/:id", StatusController, :delete) - get("/statuses", StatusController, :index) get("/moderation_log", AdminAPIController, :list_log) post("/reload_emoji", AdminAPIController, :reload_emoji) get("/stats", AdminAPIController, :stats) - get("/chats/:id", ChatController, :show) - get("/chats/:id/messages", ChatController, :messages) delete("/chats/:id/messages/:message_id", ChatController, :delete_message) end -- cgit v1.2.3 From 1c223331fc7276a7e5946b6dbd5d2b713cd6c1e8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:28:09 +0300 Subject: API: show info about privileged staff in instance metadata --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 3 ++- lib/pleroma/web/nodeinfo/nodeinfo.ex | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 7072d5d61..8e657ee0f 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -45,7 +45,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do features: features(), federation: federation(), fields_limits: fields_limits(), - post_formats: Config.get([:instance, :allowed_post_formats]) + post_formats: Config.get([:instance, :allowed_post_formats]), + privileged_staff: Config.get([:instance, :privileged_staff]) }, stats: %{mau: Pleroma.User.active_user_count()}, vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex index 3781781c8..80a2ce676 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex @@ -69,7 +69,8 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false), features: features, restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]), - skipThreadContainment: Config.get([:instance, :skip_thread_containment], false) + skipThreadContainment: Config.get([:instance, :skip_thread_containment], false), + privilegedStaff: Config.get([:instance, :privileged_staff]) } } end -- cgit v1.2.3 From f66675f349a6e6b8111280e1abd23871688f6179 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:57:54 +0300 Subject: API: fix duplicate :get_password_token route --- lib/pleroma/web/router.ex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 7ba72994b..5473cd93d 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -199,7 +199,6 @@ defmodule Pleroma.Web.Router do post("/relay", RelayController, :follow) delete("/relay", RelayController, :unfollow) - get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) patch("/users/force_password_reset", AdminAPIController, :force_password_reset) get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) -- cgit v1.2.3 From f02715c4b2bfe5b1f055e44d8fece2047d85b611 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 03:12:32 +0300 Subject: Fix lint errors --- lib/pleroma/web/plugs/ensure_staff_privileged.ex | 31 ---------------------- .../web/plugs/ensure_staff_privileged_plug.ex | 31 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) delete mode 100644 lib/pleroma/web/plugs/ensure_staff_privileged.ex create mode 100644 lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex (limited to 'lib') diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged.ex b/lib/pleroma/web/plugs/ensure_staff_privileged.ex deleted file mode 100644 index b15ddfc56..000000000 --- a/lib/pleroma/web/plugs/ensure_staff_privileged.ex +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do - @moduledoc """ - Ensures if staff are privileged enough to do certain tasks - """ - - import Pleroma.Web.TranslationHelpers - import Plug.Conn - - alias Pleroma.User - alias Pleroma.Config - - def init(options) do - options - end - - def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn - - def call(conn, _) do - if Config.get!([:instance, :privileged_staff]) do - conn - else - conn - |> render_error(:forbidden, "User is not an admin.") - |> halt() - end - end -end diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex new file mode 100644 index 000000000..fe0a11dec --- /dev/null +++ b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do + @moduledoc """ + Ensures if staff are privileged enough to do certain tasks + """ + + import Pleroma.Web.TranslationHelpers + import Plug.Conn + + alias Pleroma.Config + alias Pleroma.User + + def init(options) do + options + end + + def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn + + def call(conn, _) do + if Config.get!([:instance, :privileged_staff]) do + conn + else + conn + |> render_error(:forbidden, "User is not an admin.") + |> halt() + end + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 5473cd93d..02ca8d70a 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -238,7 +238,7 @@ defmodule Pleroma.Web.Router do delete("/users", UserController, :delete) get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) - patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) + patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) get("/users/:nickname/chats", AdminAPIController, :list_user_chats) -- cgit v1.2.3 From 08c0f09bad040ea713893be822342867f589efbe Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 27 Dec 2021 09:13:31 +0300 Subject: Made slow queries logging disabled by default. --- lib/pleroma/telemetry/logger.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 0f73ecc02..d7fea9c0f 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -106,7 +106,9 @@ defmodule Pleroma.Telemetry.Logger do ) do logging_config = Pleroma.Config.get([:telemetry, :slow_queries_logging], []) - if logging_config[:min_duration] && query_time > logging_config[:min_duration] and + if logging_config[:enabled] && + logging_config[:min_duration] && + query_time > logging_config[:min_duration] and (is_nil(logging_config[:exclude_sources]) or source not in logging_config[:exclude_sources]) do log_slow_query(measurements, metadata, config) -- cgit v1.2.3