From 50e3750758510a2790ce6229d9194ace72d1e012 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 May 2021 13:58:50 -0500 Subject: [PATCH 01/82] Add notice compatibility routes for other frontends Fixes: https://git.pleroma.social/pleroma/pleroma/-/issues/1785 --- lib/pleroma/web/router.ex | 5 ++ .../web/static_fe/static_fe_controller.ex | 9 ++++ .../web/o_status/o_status_controller_test.exs | 50 +++++++++++++++++++ .../web/plugs/frontend_static_plug_test.exs | 2 + 4 files changed, 66 insertions(+) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 72ad14f05..5e732e4bb 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -637,6 +637,11 @@ defmodule Pleroma.Web.Router do get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) + # Notice compatibility routes for other frontends + get("/@:nickname/:id", OStatus.OStatusController, :notice) + get("/@:nickname/posts/:id", OStatus.OStatusController, :notice) + get("/:nickname/status/:id", OStatus.OStatusController, :notice) + # Mastodon compatibility routes get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object) get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity) diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index fe485d10d..421070636 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -168,6 +168,15 @@ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), do: assign(conn, :notice_id, notice_id) + defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + + defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + + defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + defp assign_id(%{path_info: ["users", user_id]} = conn, _opts), do: assign(conn, :username_or_id, user_id) diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index 2038f4ddd..fab042439 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -343,4 +343,54 @@ test "does not require authentication on non-federating instances", %{ |> response(200) end end + + describe "notice compatibility routes" do + test "Soapbox FE", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/@#{user.nickname}/posts/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + + test "Mastodon", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/@#{user.nickname}/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + + test "Twitter", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/#{user.nickname}/status/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + end end diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 100b83d6a..7596a9a54 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -86,6 +86,8 @@ test "api routes are detected correctly" do "objects", "activities", "notice", + "@:nickname", + ":nickname", "users", "tags", "mailer", From b15c4629ff3093353ac5e37d381db1cdc4da1c3a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 May 2021 14:36:27 -0500 Subject: [PATCH 02/82] CHANGELOG: notice routes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb4b1e73..625cf3266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Don't crash so hard when email settings are invalid. +- Display OpenGraph data on alternative notice routes. ## Unreleased (Patch) From 079afd32d86ae5211106cdc9e1916c6640bedd39 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 23 Jun 2021 14:19:26 -0500 Subject: [PATCH 03/82] Enable :warnings_as_errors for CI only --- mix.exs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index a0a6106a9..85d5c7b55 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ def project do elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), - elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())], + elixirc_options: [warnings_as_errors: warnings_as_errors()], xref: [exclude: [:eldap]], start_permanent: Mix.env() == :prod, aliases: aliases(), @@ -90,8 +90,7 @@ defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"] defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_), do: ["lib"] - defp warnings_as_errors(:prod), do: false - defp warnings_as_errors(_), do: true + defp warnings_as_errors, do: System.get_env("CI") == "true" # Specifies OAuth dependencies. defp oauth_deps do From 4fe9a758f9d4276335631f0df84761d20e312a8a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 12 Jul 2021 22:03:32 -0500 Subject: [PATCH 04/82] Let moderators manage custom emojis --- lib/pleroma/web/router.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 919f4f510..ecb10d95c 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -265,7 +265,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/pack" do - pipe_through([:admin_api, :require_admin]) + pipe_through(:admin_api) post("/", EmojiPackController, :create) patch("/", EmojiPackController, :update) @@ -280,7 +280,7 @@ defmodule Pleroma.Web.Router do # Modifying packs scope "/packs" do - pipe_through([:admin_api, :require_admin]) + pipe_through(:admin_api) get("/import", EmojiPackController, :import_from_filesystem) get("/remote", EmojiPackController, :remote) From 2b3d7794b23aac30cf8f977009d17b1abc602d19 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 12 Jul 2021 22:24:49 -0500 Subject: [PATCH 05/82] AdminAPI: let moderators actually do things --- lib/pleroma/web/router.ex | 87 +++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ecb10d95c..d542108b4 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -159,12 +159,11 @@ defmodule Pleroma.Web.Router do post("/uploader_callback/:upload_path", UploaderController, :callback) end + # AdminAPI: only admins can perform these actions scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through([:admin_api, :require_admin]) put("/users/disable_mfa", AdminAPIController, :disable_mfa) - put("/users/tag", AdminAPIController, :tag_users) - delete("/users/tag", AdminAPIController, :untag_users) get("/users/:nickname/permission_group", AdminAPIController, :right_get) get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get) @@ -187,34 +186,17 @@ defmodule Pleroma.Web.Router do post("/users/follow", UserController, :follow) post("/users/unfollow", UserController, :unfollow) - delete("/users", UserController, :delete) post("/users", UserController, :create) - patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) - patch("/users/activate", UserController, :activate) - patch("/users/deactivate", UserController, :deactivate) - patch("/users/approve", UserController, :approve) get("/relay", RelayController, :index) post("/relay", RelayController, :follow) delete("/relay", RelayController, :unfollow) - 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, :list) - 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", AdminAPIController, :list_instance_statuses) - get("/instance_document/:name", InstanceDocumentController, :show) patch("/instance_document/:name", InstanceDocumentController, :update) delete("/instance_document/:name", InstanceDocumentController, :delete) @@ -222,6 +204,53 @@ defmodule Pleroma.Web.Router do patch("/users/confirm_email", AdminAPIController, :confirm_email) patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email) + get("/config", ConfigController, :show) + post("/config", ConfigController, :update) + get("/config/descriptions", ConfigController, :descriptions) + get("/need_reboot", AdminAPIController, :need_reboot) + get("/restart", AdminAPIController, :restart) + + get("/oauth_app", OAuthAppController, :index) + post("/oauth_app", OAuthAppController, :create) + patch("/oauth_app/:id", OAuthAppController, :update) + delete("/oauth_app/:id", OAuthAppController, :delete) + + get("/media_proxy_caches", MediaProxyCacheController, :index) + post("/media_proxy_caches/delete", MediaProxyCacheController, :delete) + post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) + + get("/frontends", FrontendController, :index) + post("/frontends/install", FrontendController, :install) + + post("/backups", AdminAPIController, :create_backup) + end + + # AdminAPI: admins and mods (staff) can perform these actions + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:admin_api) + + put("/users/tag", AdminAPIController, :tag_users) + delete("/users/tag", AdminAPIController, :untag_users) + + patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) + patch("/users/activate", UserController, :activate) + 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", UserController, :list) + 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", AdminAPIController, :list_instance_statuses) + get("/reports", ReportController, :index) get("/reports/:id", ReportController, :show) patch("/reports", ReportController, :update) @@ -233,34 +262,14 @@ defmodule Pleroma.Web.Router do delete("/statuses/:id", StatusController, :delete) get("/statuses", StatusController, :index) - get("/config", ConfigController, :show) - post("/config", ConfigController, :update) - get("/config/descriptions", ConfigController, :descriptions) - get("/need_reboot", AdminAPIController, :need_reboot) - get("/restart", AdminAPIController, :restart) - get("/moderation_log", AdminAPIController, :list_log) post("/reload_emoji", AdminAPIController, :reload_emoji) get("/stats", AdminAPIController, :stats) - get("/oauth_app", OAuthAppController, :index) - post("/oauth_app", OAuthAppController, :create) - patch("/oauth_app/:id", OAuthAppController, :update) - delete("/oauth_app/:id", OAuthAppController, :delete) - - get("/media_proxy_caches", MediaProxyCacheController, :index) - post("/media_proxy_caches/delete", MediaProxyCacheController, :delete) - post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) - get("/chats/:id", ChatController, :show) get("/chats/:id/messages", ChatController, :messages) delete("/chats/:id/messages/:message_id", ChatController, :delete_message) - - get("/frontends", FrontendController, :index) - post("/frontends/install", FrontendController, :install) - - post("/backups", AdminAPIController, :create_backup) end scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do From e311c60923432f30fc4ab7bd37d338d60f40e25f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 12 Jul 2021 22:43:16 -0500 Subject: [PATCH 06/82] CHANGELOG: moderator abilities --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc536c55..16c2b0081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time. - Attachment dimensions and blurhashes are federated when available. - Pinned posts federation +- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis ### Fixed - Don't crash so hard when email settings are invalid. From 6519732045596b1f0b0e83c365db516afba913d9 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 25 Aug 2021 21:01:04 -0600 Subject: [PATCH 07/82] GET /api/v1/apps endpoint --- .../web/api_spec/operations/app_operation.ex | 39 +++++++++++++++++++ .../controllers/app_controller.ex | 10 +++++ .../web/mastodon_api/views/app_view.ex | 4 ++ lib/pleroma/web/o_auth/app.ex | 9 +++++ lib/pleroma/web/router.ex | 2 + .../20210818023112_add_user_id_to_apps.exs | 9 +++++ 6 files changed, 73 insertions(+) create mode 100644 priv/repo/migrations/20210818023112_add_user_id_to_apps.exs diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index dfb1c7170..72032a4e0 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -13,6 +13,19 @@ def open_api_operation(action) do apply(__MODULE__, operation, []) end + @spec index_operation() :: Operation.t() + def index_operation do + %Operation{ + tags: ["Applications"], + summary: "List applications", + description: "List the OAuth applications for the current user", + operationId: "AppController.index", + responses: %{ + 200 => Operation.response("App", "application/json", index_response()), + } + } + end + @spec create_operation() :: Operation.t() def create_operation do %Operation{ @@ -145,4 +158,30 @@ defp create_response do } } end + + defp index_response do + %Schema{ + title: "AppIndexResponse", + description: "Response schema for GET /api/v1/apps", + type: :object, + properties: [%{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + }], + example: [%{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + }] + } + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a95cc52fd..38073c29a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -14,17 +14,27 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) + plug(:skip_plug, OAuthScopesPlug when action in [:index]) + plug(Pleroma.Web.ApiSpec.CastAndValidate) @local_mastodon_name "Mastodon-Local" defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation + @doc "GET /api/v1/apps" + def index(%{assigns: %{user: user}} = conn, _params) do + with apps <- App.get_user_apps(user) do + render(conn, "index.json", %{apps: apps}) + end + end + @doc "POST /api/v1/apps" def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index c406b5a27..450943aee 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -15,6 +15,10 @@ def render("index.json", %{apps: apps, count: count, page_size: page_size, admin } end + def render("index.json", %{apps: apps}) do + render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") + end + def render("show.json", %{admin: true, app: %App{} = app} = assigns) do "show.json" |> render(Map.delete(assigns, :admin)) diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 382750010..94b0e41f0 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do import Ecto.Changeset import Ecto.Query alias Pleroma.Repo + alias Pleroma.User @type t :: %__MODULE__{} @@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do field(:client_secret, :string) field(:trusted, :boolean, default: false) + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all) has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all) @@ -129,6 +132,12 @@ def search(params) do {:ok, Repo.all(query), count} end + @spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()} + def get_user_apps(%User{id: user_id}) do + from(a in __MODULE__, where: a.user_id == ^user_id) + |> Repo.all() + end + @spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} def destroy(id) do with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 74ee23c06..904439564 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -444,6 +444,8 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) + get("/apps", AppController, :index) + get("/accounts/verify_credentials", AccountController, :verify_credentials) patch("/accounts/update_credentials", AccountController, :update_credentials) diff --git a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs new file mode 100644 index 000000000..39e7fbef5 --- /dev/null +++ b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddUserIdToApps do + use Ecto.Migration + + def change do + alter table(:apps) do + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) + end + end +end From ba6914f90a3e39dd75e7775fd37cfbb6ad3d2f3b Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 26 Aug 2021 11:11:37 -0600 Subject: [PATCH 08/82] Fix formatting in app_operation.ex --- .../web/api_spec/operations/app_operation.ex | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 72032a4e0..c2221ac98 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -21,7 +21,7 @@ def index_operation do description: "List the OAuth applications for the current user", operationId: "AppController.index", responses: %{ - 200 => Operation.response("App", "application/json", index_response()), + 200 => Operation.response("App", "application/json", index_response()) } } end @@ -164,24 +164,28 @@ defp index_response do title: "AppIndexResponse", description: "Response schema for GET /api/v1/apps", type: :object, - properties: [%{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - }], - example: [%{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - }] + properties: [ + %{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + } + ], + example: [ + %{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + } + ] } end end From baa8196fc910cfdbaefd6059bdb1a8445d83f563 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 26 Aug 2021 11:55:43 -0600 Subject: [PATCH 09/82] Fix API spec, add app schema --- .../web/api_spec/operations/app_operation.ex | 33 +++---------------- lib/pleroma/web/api_spec/schemas/app.ex | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/app.ex diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index c2221ac98..71d7b9ee8 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers + alias Pleroma.Web.ApiSpec.Schemas.App @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -21,7 +22,7 @@ def index_operation do description: "List the OAuth applications for the current user", operationId: "AppController.index", responses: %{ - 200 => Operation.response("App", "application/json", index_response()) + 200 => Operation.response("Array of App", "application/json", array_of_apps()) } } end @@ -159,33 +160,7 @@ defp create_response do } end - defp index_response do - %Schema{ - title: "AppIndexResponse", - description: "Response schema for GET /api/v1/apps", - type: :object, - properties: [ - %{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - } - ], - example: [ - %{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - } - ] - } + defp array_of_apps do + %Schema{type: :array, items: App, example: [App.schema().example]} end end diff --git a/lib/pleroma/web/api_spec/schemas/app.ex b/lib/pleroma/web/api_spec/schemas/app.ex new file mode 100644 index 000000000..c3d1af3be --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/app.ex @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.App do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "App", + description: "Response schema for an app", + type: :object, + properties: %{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + }, + example: %{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + } + }) +end From eab6291094314846425339ec51fffbc94cab5501 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 11:13:25 -0600 Subject: [PATCH 10/82] Require follow and read OAuth scopes for GET /api/v1/apps --- .../web/api_spec/operations/app_operation.ex | 26 ++----------------- .../controllers/app_controller.ex | 2 +- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 71d7b9ee8..217609b01 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -36,7 +36,7 @@ def create_operation do operationId: "AppController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("App", "application/json", create_response()), + 200 => create_response(), 422 => Operation.response( "Unprocessable Entity", @@ -135,29 +135,7 @@ defp create_request do end defp create_response do - %Schema{ - title: "AppCreateResponse", - description: "Response schema for an app", - type: :object, - properties: %{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - }, - example: %{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - } - } + Operation.response("App", "application/json", App) end defp array_of_apps do diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 38073c29a..e44c4340e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do plug(:skip_auth when action in [:create, :verify_credentials]) - plug(:skip_plug, OAuthScopesPlug when action in [:index]) + plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) plug(Pleroma.Web.ApiSpec.CastAndValidate) From a14e1c0003285adce3c995f1b19a02179a556fd0 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 18:02:36 -0600 Subject: [PATCH 11/82] Move GET /api/v1/apps to GET /api/v1/pleroma/apps --- .../web/api_spec/operations/app_operation.ex | 17 ---------- .../operations/pleroma_app_operation.ex | 31 +++++++++++++++++++ .../controllers/app_controller.ex | 10 ------ .../web/mastodon_api/views/app_view.ex | 4 --- .../pleroma_api/controllers/app_controller.ex | 23 ++++++++++++++ lib/pleroma/web/pleroma_api/views/app_view.ex | 11 +++++++ lib/pleroma/web/router.ex | 3 +- 7 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex create mode 100644 lib/pleroma/web/pleroma_api/controllers/app_controller.ex create mode 100644 lib/pleroma/web/pleroma_api/views/app_view.ex diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 217609b01..5e72c4824 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -14,19 +14,6 @@ def open_api_operation(action) do apply(__MODULE__, operation, []) end - @spec index_operation() :: Operation.t() - def index_operation do - %Operation{ - tags: ["Applications"], - summary: "List applications", - description: "List the OAuth applications for the current user", - operationId: "AppController.index", - responses: %{ - 200 => Operation.response("Array of App", "application/json", array_of_apps()) - } - } - end - @spec create_operation() :: Operation.t() def create_operation do %Operation{ @@ -137,8 +124,4 @@ defp create_request do defp create_response do Operation.response("App", "application/json", App) end - - defp array_of_apps do - %Schema{type: :array, items: App, example: [App.schema().example]} - end end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex new file mode 100644 index 000000000..efaf81af0 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.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.ApiSpec.PleromaAppOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.App + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + @spec index_operation() :: Operation.t() + def index_operation do + %Operation{ + tags: ["Applications"], + summary: "List applications", + description: "List the OAuth applications for the current user", + operationId: "AppController.index", + responses: %{ + 200 => Operation.response("Array of App", "application/json", array_of_apps()) + } + } + end + + defp array_of_apps do + %Schema{type: :array, items: App, example: [App.schema().example]} + end +end \ No newline at end of file diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index e44c4340e..a95cc52fd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -14,27 +14,17 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) - plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) - plug(Pleroma.Web.ApiSpec.CastAndValidate) @local_mastodon_name "Mastodon-Local" defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation - @doc "GET /api/v1/apps" - def index(%{assigns: %{user: user}} = conn, _params) do - with apps <- App.get_user_apps(user) do - render(conn, "index.json", %{apps: apps}) - end - end - @doc "POST /api/v1/apps" def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index 450943aee..c406b5a27 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -15,10 +15,6 @@ def render("index.json", %{apps: apps, count: count, page_size: page_size, admin } end - def render("index.json", %{apps: apps}) do - render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") - end - def render("show.json", %{admin: true, app: %App{} = app} = assigns) do "show.json" |> render(Map.delete(assigns, :admin)) diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex new file mode 100644 index 000000000..6d46d917c --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppController do + use Pleroma.Web, :controller + + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation + + @doc "GET /api/v1/pleroma/apps" + def index(%{assigns: %{user: user}} = conn, _params) do + with apps <- App.get_user_apps(user) do + render(conn, "index.json", %{apps: apps}) + end + end +end \ No newline at end of file diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex new file mode 100644 index 000000000..7dd560f8f --- /dev/null +++ b/lib/pleroma/web/pleroma_api/views/app_view.ex @@ -0,0 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppView do + use Pleroma.Web, :view + + def render("index.json", %{apps: apps}) do + render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") + end +end \ No newline at end of file diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 904439564..2dba21978 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -372,6 +372,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) + get("/apps", AppController, :index) get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index) get("/statuses/:id/reactions", EmojiReactionController, :index) end @@ -444,8 +445,6 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - get("/apps", AppController, :index) - get("/accounts/verify_credentials", AccountController, :verify_credentials) patch("/accounts/update_credentials", AccountController, :update_credentials) From d02cf7b0cd550bc182e7307b90f077e159b5637f Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 18:17:09 -0600 Subject: [PATCH 12/82] Fix lint --- lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex | 2 +- lib/pleroma/web/pleroma_api/controllers/app_controller.ex | 2 +- lib/pleroma/web/pleroma_api/views/app_view.ex | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex index efaf81af0..582a169ee 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex @@ -28,4 +28,4 @@ def index_operation do defp array_of_apps do %Schema{type: :array, items: App, example: [App.schema().example]} end -end \ No newline at end of file +end diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex index 6d46d917c..d857f424f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex @@ -20,4 +20,4 @@ def index(%{assigns: %{user: user}} = conn, _params) do render(conn, "index.json", %{apps: apps}) end end -end \ No newline at end of file +end diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex index 7dd560f8f..6b5d838f5 100644 --- a/lib/pleroma/web/pleroma_api/views/app_view.ex +++ b/lib/pleroma/web/pleroma_api/views/app_view.ex @@ -8,4 +8,4 @@ defmodule Pleroma.Web.PleromaAPI.AppView do def render("index.json", %{apps: apps}) do render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") end -end \ No newline at end of file +end From 33f063204edb63344628bdfa72ff11f81ded62a9 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 23:18:12 -0600 Subject: [PATCH 13/82] Add unit test for Pleroma API app controller --- .../controllers/app_controller.ex | 14 ++++- lib/pleroma/web/o_auth/app.ex | 2 +- .../controllers/app_controller_test.exs | 53 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 test/pleroma/web/pleroma_api/controllers/app_controller_test.exs diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a95cc52fd..466508137 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -10,11 +10,15 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Maps + alias Pleroma.User alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + require Logger + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) @@ -26,13 +30,21 @@ defmodule Pleroma.Web.MastodonAPI.AppController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation @doc "POST /api/v1/apps" - def create(%{body_params: params} = conn, _params) do + def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) + user_id = + with %User{id: id} <- user do + id + else + _ -> nil + end + app_attrs = params |> Map.take([:client_name, :redirect_uris, :website]) |> Map.put(:scopes, scopes) + |> Maps.put_if_present(:user_id, user_id) with cs <- App.register_changeset(%App{}, app_attrs), false <- cs.changes[:client_name] == @local_mastodon_name, diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 94b0e41f0..dacfbadc8 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do @spec changeset(t(), map()) :: Ecto.Changeset.t() def changeset(struct, params) do - cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted]) + cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id]) end @spec register_changeset(t(), map()) :: Ecto.Changeset.t() diff --git a/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs new file mode 100644 index 000000000..5e24e18a8 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppControllerTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Push + + import Pleroma.Factory + + test "apps", %{conn: conn} do + user = insert(:user) + app_attrs = build(:oauth_app) + + creation = + conn + |> put_req_header("content-type", "application/json") + |> assign(:user, user) + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = App.get_user_apps(user) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(creation, 200) + + response = + conn + |> put_req_header("content-type", "application/json") + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read", "follow"])) + |> get("/api/v1/pleroma/apps") + |> json_response_and_validate_schema(200) + + [apps] = response + + assert length(response) == 1 + assert apps["client_id"] == app.client_id + end +end From 2e59cdd80f3e3d14c59aeba1fde2f8f9b8305e1f Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 29 Aug 2021 07:22:03 -0600 Subject: [PATCH 14/82] Fix aliases sorting --- lib/pleroma/web/mastodon_api/controllers/app_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 466508137..d2a35dce2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -11,8 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller alias Pleroma.Maps - alias Pleroma.User alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token From 3117c6099733207b7f2a777f8cb8b5b3b839ebe8 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 29 Aug 2021 07:25:54 -0600 Subject: [PATCH 15/82] Make suggested change for create_response --- lib/pleroma/web/api_spec/operations/app_operation.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 5e72c4824..2284ac127 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -23,7 +23,7 @@ def create_operation do operationId: "AppController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => create_response(), + 200 => Operation.response("App", "application/json", App), 422 => Operation.response( "Unprocessable Entity", @@ -120,8 +120,4 @@ defp create_request do } } end - - defp create_response do - Operation.response("App", "application/json", App) - end end From 555d7d57c9a408185617268ca810002cbd59f764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 9 Sep 2021 18:35:45 +0000 Subject: [PATCH 16/82] Add "exposable_reactions" to features, if showing reactions --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index ef208062b..70305b1c1 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -83,7 +83,10 @@ def features do "safe_dm_mentions" end, "pleroma_emoji_reactions", - "pleroma_chat_messages" + "pleroma_chat_messages", + if Config.get([:instance, :show_reactions]) do + "exposable_reactions" + end ] |> Enum.filter(& &1) end From 40414bf177c93b39d75c6091ef0ce1db093edb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 16:53:30 +0100 Subject: [PATCH 17/82] MastoAPI: Add user notes on accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- docs/development/API/pleroma_api.md | 6 ++- lib/pleroma/user_note.ex | 52 +++++++++++++++++++ .../api_spec/operations/account_operation.ex | 43 +++++++++++++++ lib/pleroma/web/api_spec/schemas/account.ex | 1 + .../api_spec/schemas/account_relationship.ex | 2 + lib/pleroma/web/api_spec/schemas/status.ex | 1 + .../controllers/account_controller.ex | 18 ++++++- .../web/mastodon_api/views/account_view.ex | 8 ++- lib/pleroma/web/router.ex | 1 + .../20211121000000_create_user_notes.exs | 17 ++++++ .../controllers/account_controller_test.exs | 14 +++++ .../mastodon_api/views/account_view_test.exs | 3 +- 12 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/user_note.ex create mode 100644 priv/repo/migrations/20211121000000_create_user_notes.exs diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 8f6422da0..b401a7cc7 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -162,7 +162,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` @@ -186,7 +187,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` diff --git a/lib/pleroma/user_note.ex b/lib/pleroma/user_note.ex new file mode 100644 index 000000000..5e82d359f --- /dev/null +++ b/lib/pleroma/user_note.ex @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserNote do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.UserNote + + schema "user_notes" do + belongs_to(:source, User, type: FlakeId.Ecto.CompatType) + belongs_to(:target, User, type: FlakeId.Ecto.CompatType) + field(:comment, :string) + + timestamps() + end + + def changeset(%UserNote{} = user_note, params \\ %{}) do + user_note + |> cast(params, [:source_id, :target_id, :comment]) + |> validate_required([:source_id, :target_id]) + end + + def show(%User{} = source, %User{} = target) do + with %UserNote{} = note <- + UserNote + |> where(source_id: ^source.id, target_id: ^target.id) + |> Repo.one() do + note.comment + else + _ -> "" + end + end + + def create(%User{} = source, %User{} = target, comment) do + %UserNote{} + |> changeset(%{ + source_id: source.id, + target_id: target.id, + comment: comment + }) + |> Repo.insert( + on_conflict: {:replace, [:comment]}, + conflict_target: [:source_id, :target_id] + ) + end +end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..6bec9f178 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -328,6 +328,29 @@ def unblock_operation do } end + def note_operation do + %Operation{ + tags: ["Account actions"], + summary: "Create note", + operationId: "AccountController.note", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + requestBody: request_body("Parameters", note_request()), + description: "Create a note for the given account.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :comment, + :query, + %Schema{type: :string}, + "Account note body" + ) + ], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def follow_by_uri_operation do %Operation{ tags: ["Account actions"], @@ -685,6 +708,7 @@ defp array_of_relationships do "blocked_by" => true, "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => false, "subscribing" => false, @@ -699,6 +723,7 @@ defp array_of_relationships do "blocked_by" => true, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => true, "domain_blocking" => false, "subscribing" => false, @@ -713,6 +738,7 @@ defp array_of_relationships do "blocked_by" => false, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => true, "subscribing" => true, @@ -760,6 +786,23 @@ defp mute_request do } end + defp note_request do + %Schema{ + title: "AccountNoteRequest", + description: "POST body for adding anote for an account", + type: :object, + properties: %{ + comment: %Schema{ + type: :string, + description: "Account note body", + } + }, + example: %{ + "comment" => "Example note" + } + } + end + defp array_of_lists do %Schema{ title: "ArrayOfLists", diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..e0bd2728b 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -194,6 +194,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..163066032 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -22,6 +22,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do id: FlakeID, muting: %Schema{type: :boolean}, muting_notifications: %Schema{type: :boolean}, + note: %Schema{type: :string}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, subscribing: %Schema{type: :boolean} @@ -36,6 +37,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..60801f322 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -282,6 +282,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "id" => "9toJCsKN7SmSf3aj5c", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 5fcbffc34..8a43d49d3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Maps alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline @@ -53,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do when action in [:verify_credentials, :endorsements, :identity_proofs] ) - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:update_credentials, :note] + ) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) @@ -79,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) @relationship_actions [:follow, :unfollow] - @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a + @needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a plug( RateLimiter, @@ -435,6 +440,15 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do end end + @doc "POST /api/v1/accounts/:id/note" + def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + with {:ok, _user_note} <- UserNote.create(noter, target, comment) do + render(conn, "relationship.json", user: noter, target: target) + else + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + @doc "POST /api/v1/follows" def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do case User.get_cached_by_nickname(uri) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..a3a9f9548 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do alias Pleroma.FollowingRelationship alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView @@ -156,7 +157,12 @@ def render( target, &User.muting_reblogs?(&1, &2) ), - endorsed: false + endorsed: false, + note: + UserNote.show( + reading_user, + target + ) } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index abb332ec2..ca5db8ea3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -456,6 +456,7 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/unblock", AccountController, :unblock) post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) + post("/accounts/:id/note", AccountController, :note) get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs new file mode 100644 index 000000000..8fc23749f --- /dev/null +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateUserNotes do + use Ecto.Migration + + def change do + create_if_not_exists table(:user_notes) do + add(:source_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:target_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:comment, :string) + + timestamps() + end + + create_if_not_exists( + unique_index(:user_notes, [:source_id, :target_id]) + ) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index a92a58224..48e658dd2 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1776,4 +1776,18 @@ test "getting a list of blocks" do assert [%{"id" => ^id2}] = result end + + test "create a note on a user" do + %{conn: conn} = oauth_access(["write:accounts"]) + other_user = insert(:user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) + + assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + end end diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..9fe9d73bc 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -271,7 +271,8 @@ defp test_relationship_rendering(user, other_user, expected_result) do requested: false, domain_blocking: false, showing_reblogs: true, - endorsed: false + endorsed: false, + note: "" } test "represent a relationship for the following and followed user" do From 106b5c26781dd1e92b6cd820b3dff41a27a4c4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:36:37 +0100 Subject: [PATCH 18/82] Fix a typo 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6bec9f178..6cedada2c 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -789,7 +789,7 @@ defp mute_request do defp note_request do %Schema{ title: "AccountNoteRequest", - description: "POST body for adding anote for an account", + description: "POST body for adding a note for an account", type: :object, properties: %{ comment: %Schema{ From cb76faece99c706685b71ad5a13943036b481645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:50:42 +0100 Subject: [PATCH 19/82] Update test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../controllers/account_controller_test.exs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 48e658dd2..4f855ac5c 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1778,16 +1778,19 @@ test "getting a list of blocks" do end test "create a note on a user" do - %{conn: conn} = oauth_access(["write:accounts"]) + %{conn: conn} = oauth_access(["write:accounts", "read:follows"]) other_user = insert(:user) - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{other_user.id}/note", %{ - "comment" => "Example note" - }) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) - assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + assert [%{"note" => "Example note"}] = + conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/accounts/relationships?id=#{other_user.id}") + |> json_response_and_validate_schema(200) end end From 8e040e098b1176098123e52608a9a73adec2b5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 18:17:06 +0100 Subject: [PATCH 20/82] Lint 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 | 2 +- .../web/mastodon_api/controllers/account_controller.ex | 5 ++++- priv/repo/migrations/20211121000000_create_user_notes.exs | 4 +--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6cedada2c..4aca16e72 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -794,7 +794,7 @@ defp note_request do properties: %{ comment: %Schema{ type: :string, - description: "Account note body", + description: "Account note body" } }, example: %{ diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 8a43d49d3..a2c4920c1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -441,7 +441,10 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do end @doc "POST /api/v1/accounts/:id/note" - def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + def note( + %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, + _params + ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) else diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs index 8fc23749f..b75e11695 100644 --- a/priv/repo/migrations/20211121000000_create_user_notes.exs +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -10,8 +10,6 @@ def change do timestamps() end - create_if_not_exists( - unique_index(:user_notes, [:source_id, :target_id]) - ) + create_if_not_exists(unique_index(:user_notes, [:source_id, :target_id])) end end From 588bcbac55ebbaa1ea68792a1f60aa92c9915f69 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 22 Nov 2021 10:54:44 +0000 Subject: [PATCH 21/82] Apply 2 suggestion(s) to 2 file(s) --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 4aca16e72..8613f3a98 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -331,7 +331,7 @@ def unblock_operation do def note_operation do %Operation{ tags: ["Account actions"], - summary: "Create note", + summary: "Set a private note about a user.", operationId: "AccountController.note", security: [%{"oAuth" => ["follow", "write:accounts"]}], requestBody: request_body("Parameters", note_request()), diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index a2c4920c1..5dfbecf5a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -447,8 +447,6 @@ def note( ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) - else - {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end From d64d1b1d452e954cba92d686f9a6b8dea2d304a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 23 Nov 2021 11:31:09 +0100 Subject: [PATCH 22/82] Fix replies count for remote replies 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/side_effects.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 701181a14..d55a4b340 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -200,7 +200,7 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) - if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do + if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) end From cb9359335f6b0e1d19fb82e4045740d30767254c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 24 Nov 2021 16:45:05 -0600 Subject: [PATCH 23/82] Expose /manifest.json for PWA --- lib/pleroma/web/manifest_controller.ex | 14 ++++++++++ lib/pleroma/web/router.ex | 6 ++++ lib/pleroma/web/views/manifest_view.ex | 28 +++++++++++++++++++ test/pleroma/web/manifest_controller_test.exs | 17 +++++++++++ .../web/plugs/frontend_static_plug_test.exs | 1 + 5 files changed, 66 insertions(+) create mode 100644 lib/pleroma/web/manifest_controller.ex create mode 100644 lib/pleroma/web/views/manifest_view.ex create mode 100644 test/pleroma/web/manifest_controller_test.exs diff --git a/lib/pleroma/web/manifest_controller.ex b/lib/pleroma/web/manifest_controller.ex new file mode 100644 index 000000000..52589540b --- /dev/null +++ b/lib/pleroma/web/manifest_controller.ex @@ -0,0 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ManifestController do + use Pleroma.Web, :controller + + plug(:skip_auth when action == :show) + + @doc "GET /manifest.json" + def show(conn, _params) do + render(conn, "manifest.json") + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index efca7078a..c3b74e622 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -750,6 +750,12 @@ defmodule Pleroma.Web.Router do get("/web/manifest.json", MastoFEController, :manifest) end + scope "/", Pleroma.Web do + pipe_through(:api) + + get("/manifest.json", ManifestController, :show) + end + scope "/", Pleroma.Web do pipe_through(:mastodon_html) diff --git a/lib/pleroma/web/views/manifest_view.ex b/lib/pleroma/web/views/manifest_view.ex new file mode 100644 index 000000000..cc78ea347 --- /dev/null +++ b/lib/pleroma/web/views/manifest_view.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ManifestView do + use Pleroma.Web, :view + alias Pleroma.Config + alias Pleroma.Web.Endpoint + + def render("manifest.json", _params) do + %{ + name: Config.get([:instance, :name]), + description: Config.get([:instance, :description]), + icons: Config.get([:manifest, :icons]), + theme_color: Config.get([:manifest, :theme_color]), + background_color: Config.get([:manifest, :background_color]), + display: "standalone", + scope: Endpoint.url(), + start_url: "/", + categories: [ + "social" + ], + serviceworker: %{ + src: "/sw.js" + } + } + end +end diff --git a/test/pleroma/web/manifest_controller_test.exs b/test/pleroma/web/manifest_controller_test.exs new file mode 100644 index 000000000..b7a4940db --- /dev/null +++ b/test/pleroma/web/manifest_controller_test.exs @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ManifestControllerTest do + use Pleroma.Web.ConnCase + + setup do + clear_config([:instance, :name], "Manifest Test") + clear_config([:manifest, :theme_color], "#ff0000") + end + + test "manifest.json", %{conn: conn} do + conn = get(conn, "/manifest.json") + assert %{"name" => "Manifest Test", "theme_color" => "#ff0000"} = json_response(conn, 200) + end +end diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 4152cdefe..a1cce6398 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -95,6 +95,7 @@ test "api routes are detected correctly" do ".well-known", "nodeinfo", "web", + "manifest.json", "auth", "embed", "proxy", From 7e1caddc58bc6850f9780a9cd432b4b839f02e90 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 25 Nov 2021 14:57:36 -0600 Subject: [PATCH 24/82] v2 Suggestions: return empty array --- .../controllers/suggestion_controller.ex | 17 ++++++++++++++++- lib/pleroma/web/router.ex | 2 ++ .../controllers/suggestion_controller_test.exs | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 01e122dd9..b941849f5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2]) def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -26,7 +26,22 @@ def index_operation do } end + def index2_operation do + %OpenApiSpex.Operation{ + tags: ["Suggestions"], + summary: "Follow suggestions (Not implemented)", + operationId: "SuggestionController.index2", + responses: %{ + 200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response() + } + } + end + @doc "GET /api/v1/suggestions" def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) + + @doc "GET /api/v2/suggestions" + def index2(conn, params), + do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index efca7078a..acf9ce6c2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -594,6 +594,8 @@ defmodule Pleroma.Web.Router do get("/search", SearchController, :search2) post("/media", MediaController, :create2) + + get("/suggestions", SuggestionController, :index2) end scope "/api", Pleroma.Web do diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 168966fc9..5a9aea680 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -15,4 +15,13 @@ test "returns empty result", %{conn: conn} do assert res == [] end + + test "returns empty result (v2)", %{conn: conn} do + res = + conn + |> get("/api/v2/suggestions") + |> json_response_and_validate_schema(200) + + assert res == [] + end end From b17360cd7c92d8b2337fa4fd175c3e1312eb352e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 14:33:27 -0600 Subject: [PATCH 25/82] v2 Suggestions: rudimentary API response --- lib/pleroma/user.ex | 1 + lib/pleroma/user/query.ex | 4 +++ .../controllers/suggestion_controller.ex | 15 ++++++-- .../web/mastodon_api/views/suggestion_view.ex | 28 +++++++++++++++ .../20211126191138_add_suggestions.exs | 9 +++++ test/pleroma/user/query_test.exs | 10 ++++++ .../suggestion_controller_test.exs | 7 ++-- .../views/suggestion_view_test.exs | 34 +++++++++++++++++++ 8 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 lib/pleroma/web/mastodon_api/views/suggestion_view.ex create mode 100644 priv/repo/migrations/20211126191138_add_suggestions.exs create mode 100644 test/pleroma/web/mastodon_api/views/suggestion_view_test.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 62506f37a..6d62e9b43 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -149,6 +149,7 @@ defmodule Pleroma.User do field(:last_active_at, :naive_datetime) field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) + field(:is_suggested, :boolean, default: false) embeds_one( :notification_settings, diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index ac807fc79..334e395fb 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -167,6 +167,10 @@ defp compose_query({:unconfirmed, _}, query) do where(query, [u], u.is_confirmed == false) end + defp compose_query({:is_suggested, bool}, query) do + where(query, [u], u.is_suggested == ^bool) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index b941849f5..a34da98df 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller + alias Pleroma.User require Logger @@ -29,7 +30,7 @@ def index_operation do def index2_operation do %OpenApiSpex.Operation{ tags: ["Suggestions"], - summary: "Follow suggestions (Not implemented)", + summary: "Follow suggestions", operationId: "SuggestionController.index2", responses: %{ 200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response() @@ -42,6 +43,14 @@ def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) @doc "GET /api/v2/suggestions" - def index2(conn, params), - do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) + def index2(conn, params) do + limit = Map.get(params, :limit, 40) |> min(80) + + users = + %{is_suggested: true, limit: limit} + |> User.Query.build() + |> Pleroma.Repo.all() + + render(conn, "index.json", %{users: users, source: :staff, skip_visibility_check: true}) + end end diff --git a/lib/pleroma/web/mastodon_api/views/suggestion_view.ex b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex new file mode 100644 index 000000000..865229a88 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SuggestionView do + use Pleroma.Web, :view + alias Pleroma.Web.MastodonAPI.AccountView + + @source_types [:staff, :global, :past_interactions] + + def render("index.json", %{users: users} = opts) do + Enum.map(users, fn user -> + opts = + opts + |> Map.put(:user, user) + |> Map.delete(:users) + + render("show.json", opts) + end) + end + + def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do + %{ + source: source, + account: AccountView.render("show.json", opts) + } + end +end diff --git a/priv/repo/migrations/20211126191138_add_suggestions.exs b/priv/repo/migrations/20211126191138_add_suggestions.exs new file mode 100644 index 000000000..5ad604e9d --- /dev/null +++ b/priv/repo/migrations/20211126191138_add_suggestions.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddSuggestions do + use Ecto.Migration + + def change do + alter table(:users) do + add(:is_suggested, :boolean, default: false, null: false) + end + end +end diff --git a/test/pleroma/user/query_test.exs b/test/pleroma/user/query_test.exs index 357016e3e..363da7665 100644 --- a/test/pleroma/user/query_test.exs +++ b/test/pleroma/user/query_test.exs @@ -34,4 +34,14 @@ test "it returns internal users when enabled" do assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 end end + + test "is_suggested param" do + _user1 = insert(:user, is_suggested: false) + user2 = insert(:user, is_suggested: true) + + assert [^user2] = + %{is_suggested: true} + |> User.Query.build() + |> Repo.all() + end end diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 5a9aea680..407063fa1 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do use Pleroma.Web.ConnCase, async: true + import Pleroma.Factory setup do: oauth_access(["read"]) @@ -16,12 +17,14 @@ test "returns empty result", %{conn: conn} do assert res == [] end - test "returns empty result (v2)", %{conn: conn} do + test "returns v2 suggestions", %{conn: conn} do + %{id: user_id} = insert(:user, is_suggested: true) + res = conn |> get("/api/v2/suggestions") |> json_response_and_validate_schema(200) - assert res == [] + assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res end end diff --git a/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs b/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs new file mode 100644 index 000000000..5aae36ce9 --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/suggestion_view_test.exs @@ -0,0 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SuggestionViewTest do + use Pleroma.DataCase, async: true + import Pleroma.Factory + alias Pleroma.Web.MastodonAPI.SuggestionView, as: View + + test "show.json" do + user = insert(:user, is_suggested: true) + json = View.render("show.json", %{user: user, source: :staff, skip_visibility_check: true}) + + assert json.source == :staff + assert json.account.id == user.id + end + + test "index.json" do + user1 = insert(:user, is_suggested: true) + user2 = insert(:user, is_suggested: true) + user3 = insert(:user, is_suggested: true) + + [suggestion1, suggestion2, suggestion3] = + View.render("index.json", %{ + users: [user1, user2, user3], + source: :staff, + skip_visibility_check: true + }) + + assert suggestion1.source == :staff + assert suggestion2.account.id == user2.id + assert suggestion3.account.url == user3.ap_id + end +end From e28d990ecba287d5c44ed04c0039b43c8f309e50 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 14:46:29 -0600 Subject: [PATCH 26/82] v2 Suggestions: don't skip visibility check --- .../web/mastodon_api/controllers/suggestion_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index a34da98df..4f92c1f46 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -43,7 +43,7 @@ def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) @doc "GET /api/v2/suggestions" - def index2(conn, params) do + def index2(%{assigns: %{user: user}} = conn, params) do limit = Map.get(params, :limit, 40) |> min(80) users = @@ -51,6 +51,6 @@ def index2(conn, params) do |> User.Query.build() |> Pleroma.Repo.all() - render(conn, "index.json", %{users: users, source: :staff, skip_visibility_check: true}) + render(conn, "index.json", %{users: users, source: :staff, for: user}) end end From 6c0484d571e4ed4e39fa3f88e6e1d2d7b8de96fa Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 15:19:01 -0600 Subject: [PATCH 27/82] AdminAPI: suggest a user through the API --- docs/development/API/admin_api.md | 40 +++++++++++ lib/pleroma/moderation_log.ex | 20 ++++++ lib/pleroma/user.ex | 16 +++++ .../admin_api/controllers/user_controller.ex | 30 ++++++++- .../operations/admin/user_operation.ex | 66 ++++++++++++++++++- lib/pleroma/web/router.ex | 3 + test/pleroma/user_test.exs | 32 +++++++++ .../controllers/user_controller_test.exs | 52 +++++++++++++++ 8 files changed, 257 insertions(+), 2 deletions(-) diff --git a/docs/development/API/admin_api.md b/docs/development/API/admin_api.md index 8f855d251..79531c45b 100644 --- a/docs/development/API/admin_api.md +++ b/docs/development/API/admin_api.md @@ -261,6 +261,46 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret } ``` +## `PATCH /api/v1/pleroma/admin/users/suggest` + +### Suggest a user + +Adds the user(s) to follower recommendations. + +- Params: + - `nicknames`: nicknames array +- Response: + +```json +{ + users: [ + { + // user object + } + ] +} +``` + +## `PATCH /api/v1/pleroma/admin/users/unsuggest` + +### Unsuggest a user + +Removes the user(s) from follower recommendations. + +- Params: + - `nicknames`: nicknames array +- Response: + +```json +{ + users: [ + { + // user object + } + ] +} +``` + ## `GET /api/v1/pleroma/admin/users/:nickname_or_id` ### Retrive the details of a user diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 1849cacc8..ca032657c 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -338,6 +338,26 @@ def get_log_entry_message(%ModerationLog{ "@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}" end + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "add_suggestion", + "subject" => users + } + }) do + "@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}" + end + + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "remove_suggestion", + "subject" => users + } + }) do + "@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}" + end + def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 6d62e9b43..880f027bc 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1678,6 +1678,22 @@ def confirm(%User{is_confirmed: false} = user) do def confirm(%User{} = user), do: {:ok, user} + def set_suggestion(users, is_suggested) when is_list(users) do + Repo.transaction(fn -> + Enum.map(users, fn user -> + with {:ok, user} <- set_suggestion(user, is_suggested), do: user + end) + end) + end + + def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user} + + def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do + user + |> change(is_suggested: is_suggested) + |> update_and_set_cache() + end + def update_notification_settings(%User{} = user, settings) do user |> cast(%{notification_settings: settings}, []) diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index 637a0e702..50208a8b7 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do :toggle_activation, :activate, :deactivate, - :approve + :approve, + :suggest, + :unsuggest ] ) @@ -239,6 +241,32 @@ def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = c render(conn, "index.json", users: updated_users) end + def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.set_suggestion(users, true) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "add_suggestion" + }) + + render(conn, "index.json", users: updated_users) + end + + def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.set_suggestion(users, false) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "remove_suggestion" + }) + + render(conn, "index.json", users: updated_users) + end + def index(conn, params) do {page, page_size} = page_params(params) filters = maybe_parse_filters(params[:filters]) diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex index c9d0bfd7c..57fb1ad65 100644 --- a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex @@ -216,7 +216,71 @@ def approve_operation do request_body( "Parameters", %Schema{ - description: "POST body for deleting multiple users", + description: "POST body for approving multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def suggest_operation do + %Operation{ + tags: ["User administration"], + summary: "Suggest multiple users", + operationId: "AdminAPI.UserController.suggest", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for adding multiple suggested users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def unsuggest_operation do + %Operation{ + tags: ["User administration"], + summary: "Unsuggest multiple users", + operationId: "AdminAPI.UserController.unsuggest", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for removing multiple suggested users", type: :object, properties: %{ nicknames: %Schema{ diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index acf9ce6c2..1f51bf456 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -194,6 +194,9 @@ defmodule Pleroma.Web.Router do patch("/users/deactivate", UserController, :deactivate) patch("/users/approve", UserController, :approve) + patch("/users/suggest", UserController, :suggest) + patch("/users/unsuggest", UserController, :unsuggest) + get("/relay", RelayController, :index) post("/relay", RelayController, :follow) delete("/relay", RelayController, :unfollow) diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 4021a565d..c6282db78 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -1720,6 +1720,38 @@ test "delete/1 purges a remote user" do assert user.banner == %{} end + describe "set_suggestion" do + test "suggests a user" do + user = insert(:user, is_suggested: false) + refute user.is_suggested + {:ok, user} = User.set_suggestion(user, true) + assert user.is_suggested + end + + test "suggests a list of users" do + unsuggested_users = [ + insert(:user, is_suggested: false), + insert(:user, is_suggested: false), + insert(:user, is_suggested: false) + ] + + {:ok, users} = User.set_suggestion(unsuggested_users, true) + + assert Enum.count(users) == 3 + + Enum.each(users, fn user -> + assert user.is_suggested + end) + end + + test "unsuggests a user" do + user = insert(:user, is_suggested: true) + assert user.is_suggested + {:ok, user} = User.set_suggestion(user, false) + refute user.is_suggested + end + end + test "get_public_key_for_ap_id fetches a user that's not in the db" do assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") end diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index d9da34f6e..df13f00e6 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -873,6 +873,58 @@ test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" end + test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do + user1 = insert(:user, is_suggested: false) + user2 = insert(:user, is_suggested: false) + + _response = + conn + |> put_req_header("content-type", "application/json") + |> patch( + "/api/pleroma/admin/users/suggest", + %{nicknames: [user1.nickname, user2.nickname]} + ) + |> json_response_and_validate_schema(200) + + [user1, user2] = Repo.reload!([user1, user2]) + + assert user1.is_suggested + assert user2.is_suggested + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{ + user2.nickname + }" + end + + test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do + user1 = insert(:user, is_suggested: true) + user2 = insert(:user, is_suggested: true) + + _response = + conn + |> put_req_header("content-type", "application/json") + |> patch( + "/api/pleroma/admin/users/unsuggest", + %{nicknames: [user1.nickname, user2.nickname]} + ) + |> json_response_and_validate_schema(200) + + [user1, user2] = Repo.reload!([user1, user2]) + + refute user1.is_suggested + refute user2.is_suggested + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{ + user2.nickname + }" + end + test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do user = insert(:user) From da06e1a17fe45407cd82f83223dc68b8920e1fe8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 15:32:01 -0600 Subject: [PATCH 28/82] v2 Suggestions: add index on is_suggested column --- priv/repo/migrations/20211126191138_add_suggestions.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/priv/repo/migrations/20211126191138_add_suggestions.exs b/priv/repo/migrations/20211126191138_add_suggestions.exs index 5ad604e9d..7cc67d8ef 100644 --- a/priv/repo/migrations/20211126191138_add_suggestions.exs +++ b/priv/repo/migrations/20211126191138_add_suggestions.exs @@ -5,5 +5,7 @@ def change do alter table(:users) do add(:is_suggested, :boolean, default: false, null: false) end + + create_if_not_exists(index(:users, [:is_suggested])) end end From aee55b9a8bc3e643377d5843a1ff5d379aecf0e3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 20:19:29 -0600 Subject: [PATCH 29/82] v2 Suggestions: dismiss a suggestion --- lib/pleroma/ecto_enums.ex | 3 +- .../controllers/suggestion_controller.ex | 30 +++++++++++++++++++ lib/pleroma/web/router.ex | 1 + .../controllers/user_controller_test.exs | 8 ++--- .../suggestion_controller_test.exs | 15 +++++++++- 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex index 2a9addabc..0e3e1e5de 100644 --- a/lib/pleroma/ecto_enums.ex +++ b/lib/pleroma/ecto_enums.ex @@ -9,7 +9,8 @@ mute: 2, reblog_mute: 3, notification_mute: 4, - inverse_subscription: 5 + inverse_subscription: 5, + suggestion_dismiss: 6 ) defenum(Pleroma.FollowingRelationship.State, diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 4f92c1f46..4ebfc737c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -5,11 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller alias Pleroma.User + alias Pleroma.UserRelationship require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2]) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss]) def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -38,6 +40,26 @@ def index2_operation do } end + def dismiss_operation do + %OpenApiSpex.Operation{ + tags: ["Suggestions"], + summary: "Remove a suggestion", + operationId: "SuggestionController.dismiss", + parameters: [ + OpenApiSpex.Operation.parameter( + :account_id, + :path, + %OpenApiSpex.Schema{type: :string}, + "Account to dismiss", + required: true + ) + ], + responses: %{ + 200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response() + } + } + end + @doc "GET /api/v1/suggestions" def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) @@ -53,4 +75,12 @@ def index2(%{assigns: %{user: user}} = conn, params) do render(conn, "index.json", %{users: users, source: :staff, for: user}) end + + @doc "DELETE /api/v1/suggestions/:account_id" + def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do + with %User{} = target <- User.get_cached_by_id(user_id), + {:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do + json(conn, %{}) + end + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 1f51bf456..5d3a17b98 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -539,6 +539,7 @@ defmodule Pleroma.Web.Router do delete("/push/subscription", SubscriptionController, :delete) get("/suggestions", SuggestionController, :index) + delete("/suggestions/:account_id", SuggestionController, :dismiss) get("/timelines/home", TimelineController, :home) get("/timelines/direct", TimelineController, :direct) diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index df13f00e6..1580ca448 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -894,9 +894,7 @@ test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do log_entry = Repo.one(ModerationLog) assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{ - user2.nickname - }" + "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}" end test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do @@ -920,9 +918,7 @@ test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do log_entry = Repo.one(ModerationLog) assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{ - user2.nickname - }" + "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}" end test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 407063fa1..803a38c67 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -4,9 +4,10 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do use Pleroma.Web.ConnCase, async: true + alias Pleroma.UserRelationship import Pleroma.Factory - setup do: oauth_access(["read"]) + setup do: oauth_access(["read", "write"]) test "returns empty result", %{conn: conn} do res = @@ -27,4 +28,16 @@ test "returns v2 suggestions", %{conn: conn} do assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res end + + test "dismiss suggestion", %{conn: conn, user: source} do + target = insert(:user, is_suggested: true) + + res = + conn + |> delete("/api/v1/suggestions/#{target.id}") + |> json_response_and_validate_schema(200) + + assert res == %{} + assert UserRelationship.exists?(:suggestion_dismiss, source, target) + end end From 437c1a5a52d6fdde3dd8ce62b3eb4c8d8507b05e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 21:02:14 -0600 Subject: [PATCH 30/82] v2 Suggestions: actually flter out dismissed suggestions --- lib/pleroma/user/query.ex | 1 + .../controllers/suggestion_controller.ex | 20 +++++++++++--- .../suggestion_controller_test.exs | 27 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 334e395fb..6d4a4ead6 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -46,6 +46,7 @@ defmodule Pleroma.User.Query do unconfirmed: boolean(), is_admin: boolean(), is_moderator: boolean(), + is_suggested: boolean(), super_users: boolean(), invisible: boolean(), internal: boolean(), diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 4ebfc737c..3c5a07b7d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller + import Ecto.Query alias Pleroma.User alias Pleroma.UserRelationship @@ -65,15 +66,28 @@ def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) @doc "GET /api/v2/suggestions" - def index2(%{assigns: %{user: user}} = conn, params) do + def index2(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do limit = Map.get(params, :limit, 40) |> min(80) users = - %{is_suggested: true, limit: limit} + %{is_suggested: true, invisible: false, limit: limit} |> User.Query.build() + |> where([u], u.id != ^user_id) + |> join(:left, [u], r in UserRelationship, + as: :relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in [:block, :mute, :suggestion_dismiss] + ) + |> where([relationships: r], is_nil(r.target_id)) |> Pleroma.Repo.all() - render(conn, "index.json", %{users: users, source: :staff, for: user}) + render(conn, "index.json", %{ + users: users, + source: :staff, + for: user, + skip_visibility_check: true + }) end @doc "DELETE /api/v1/suggestions/:account_id" diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 803a38c67..8948a52de 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -29,6 +29,33 @@ test "returns v2 suggestions", %{conn: conn} do assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res end + test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do + %{id: user_id} = insert(:user, is_suggested: true) + + conn + |> delete("/api/v1/suggestions/#{user_id}") + |> json_response_and_validate_schema(200) + + res = + conn + |> get("/api/v2/suggestions") + |> json_response_and_validate_schema(200) + + assert [] = res + end + + test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do + blocked = insert(:user, is_suggested: true) + {:ok, _} = Pleroma.Web.CommonAPI.block(blocker, blocked) + + res = + conn + |> get("/api/v2/suggestions") + |> json_response_and_validate_schema(200) + + assert [] = res + end + test "dismiss suggestion", %{conn: conn, user: source} do target = insert(:user, is_suggested: true) From e5a7547fbe1c8eccafabc458e1cbec1461bcbc9c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 21:42:28 -0600 Subject: [PATCH 31/82] v2 Suggestions: also filter out users you follow --- .../controllers/suggestion_controller.ex | 38 ++++++++++++++----- .../suggestion_controller_test.exs | 15 +++++++- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 3c5a07b7d..e913fcf4b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller import Ecto.Query + alias Pleroma.FollowingRelationship alias Pleroma.User alias Pleroma.UserRelationship @@ -66,20 +67,15 @@ def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) @doc "GET /api/v2/suggestions" - def index2(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do + def index2(%{assigns: %{user: user}} = conn, params) do limit = Map.get(params, :limit, 40) |> min(80) users = %{is_suggested: true, invisible: false, limit: limit} |> User.Query.build() - |> where([u], u.id != ^user_id) - |> join(:left, [u], r in UserRelationship, - as: :relationships, - on: - r.target_id == u.id and r.source_id == ^user_id and - r.relationship_type in [:block, :mute, :suggestion_dismiss] - ) - |> where([relationships: r], is_nil(r.target_id)) + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute, :suggestion_dismiss]) + |> exclude_following(user) |> Pleroma.Repo.all() render(conn, "index.json", %{ @@ -90,6 +86,30 @@ def index2(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do }) end + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_following(query, %User{id: user_id}) do + query + |> join(:left, [u], r in FollowingRelationship, + as: :following_relationships, + on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept + ) + |> where([following_relationships: r], is_nil(r.following_id)) + end + @doc "DELETE /api/v1/suggestions/:account_id" def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do with %User{} = target <- User.get_cached_by_id(user_id), diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 8948a52de..89273e67b 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.UserRelationship + alias Pleroma.Web.CommonAPI import Pleroma.Factory setup do: oauth_access(["read", "write"]) @@ -46,7 +47,19 @@ test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do blocked = insert(:user, is_suggested: true) - {:ok, _} = Pleroma.Web.CommonAPI.block(blocker, blocked) + {:ok, _} = CommonAPI.block(blocker, blocked) + + res = + conn + |> get("/api/v2/suggestions") + |> json_response_and_validate_schema(200) + + assert [] = res + end + + test "returns v2 suggestions excluding followed accounts", %{conn: conn, user: follower} do + followed = insert(:user, is_suggested: true) + {:ok, _, _, _} = CommonAPI.follow(follower, followed) res = conn From 8dc1d2201a21d88090c114b59e1d06f76db66897 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 22:45:49 -0600 Subject: [PATCH 32/82] Instance: add v2_suggestions feature --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 3528185d5..8284f93f5 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -59,6 +59,7 @@ def features do "mastodon_api", "mastodon_api_streaming", "polls", + "v2_suggestions", "pleroma_explicit_addressing", "shareable_emoji_packs", "multifetch", From 6519f59d91d858273f929dc1c2a36752f6db07a9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 26 Nov 2021 23:10:01 -0600 Subject: [PATCH 33/82] v2 Suggestions: return `is_suggested` through the API --- lib/pleroma/web/admin_api/views/account_view.ex | 1 + lib/pleroma/web/mastodon_api/views/account_view.ex | 1 + .../web/admin_api/controllers/user_controller_test.exs | 7 +++++-- test/pleroma/web/mastodon_api/views/account_view_test.exs | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index fae0c07f0..2f1f7e627 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -80,6 +80,7 @@ def render("show.json", %{user: user}) do "tags" => user.tags || [], "is_confirmed" => user.is_confirmed, "is_approved" => user.is_approved, + "is_suggested" => user.is_suggested, "url" => user.uri || user.ap_id, "registration_reason" => user.registration_reason, "actor_type" => user.actor_type, diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..6114e12b1 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -269,6 +269,7 @@ defp do_render("show.json", %{user: user} = opts) do ap_id: user.ap_id, also_known_as: user.also_known_as, is_confirmed: user.is_confirmed, + is_suggested: user.is_suggested, tags: user.tags, hide_followers_count: user.hide_followers_count, hide_follows_count: user.hide_follows_count, diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index 1580ca448..b199fa704 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -877,7 +877,7 @@ test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do user1 = insert(:user, is_suggested: false) user2 = insert(:user, is_suggested: false) - _response = + response = conn |> put_req_header("content-type", "application/json") |> patch( @@ -886,6 +886,7 @@ test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do ) |> json_response_and_validate_schema(200) + assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true] [user1, user2] = Repo.reload!([user1, user2]) assert user1.is_suggested @@ -901,7 +902,7 @@ test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do user1 = insert(:user, is_suggested: true) user2 = insert(:user, is_suggested: true) - _response = + response = conn |> put_req_header("content-type", "application/json") |> patch( @@ -910,6 +911,7 @@ test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do ) |> json_response_and_validate_schema(200) + assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false] [user1, user2] = Repo.reload!([user1, user2]) refute user1.is_suggested @@ -954,6 +956,7 @@ defp user_response(user, attrs \\ %{}) do "display_name" => HTML.strip_tags(user.name || user.nickname), "is_confirmed" => true, "is_approved" => true, + "is_suggested" => false, "url" => user.ap_id, "registration_reason" => nil, "actor_type" => "Person", diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..9af588778 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -83,6 +83,7 @@ test "Represent a user account" do tags: [], is_admin: false, is_moderator: false, + is_suggested: false, hide_favorites: true, hide_followers: false, hide_follows: false, @@ -183,6 +184,7 @@ test "Represent a Service(bot) account" do tags: [], is_admin: false, is_moderator: false, + is_suggested: false, hide_favorites: true, hide_followers: false, hide_follows: false, From cd5fb84b76a51fe6c7b5d672298a87c34737c303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 22 Nov 2021 19:44:30 +0100 Subject: [PATCH 34/82] remote_interaction API endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../operations/twitter_util_operation.ex | 26 +++++++++++++++++++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 9 +++++++ 3 files changed, 36 insertions(+) diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index ebcfd3be2..1a2dbb166 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -237,4 +237,30 @@ def remote_subscribe_operation do responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} } end + + def remote_interaction_operation do + %Operation{ + tags: ["Accounts"], + summary: "Remote interaction", + operationId: "UtilController.remote_interaction", + requestBody: request_body("Parameters", remote_interaction_request(), required: true), + responses: %{ + 200 => + Operation.response("Remote interaction URL", "application/json", %Schema{type: :object}) + } + } + end + + defp remote_interaction_request do + %Schema{ + title: "RemoteInteractionRequest", + description: "POST body for remote interaction", + type: :object, + required: [:ap_id, :profile], + properties: %{ + ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"}, + profile: %Schema{type: :string, description: "Remote profile webfinger"} + } + } + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index abb332ec2..f8bafd3c2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -150,6 +150,7 @@ defmodule Pleroma.Web.Router do get("/emoji", UtilController, :emoji) get("/captcha", UtilController, :captcha) get("/healthcheck", UtilController, :healthcheck) + post("/remote_interaction", UtilController, :remote_interaction) end scope "/api/v1/pleroma", Pleroma.Web do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ef43f7682..cbcef7475 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -62,6 +62,15 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil end end + def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do + conn + |> json(%{url: String.replace(template, "{uri}", ap_id)}) + else + _e -> json(conn, %{error: "Couldn't find user"}) + end + end + def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end From 949a53e327fa2d4ca2099cd4ca6fa2e3fd9e789a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 5 Dec 2021 17:46:56 -0500 Subject: [PATCH 35/82] Log Ecto queries > 500ms --- lib/pleroma/telemetry/logger.ex | 45 +++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) 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 @@ def handle_event( 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 From 64a4c147b1836be8af0c87b23073ed82bb9cf67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 18:00:58 +0100 Subject: [PATCH 36/82] MastoAPI: accept notify param in follow request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../web/api_spec/operations/account_operation.ex | 6 ++++++ lib/pleroma/web/mastodon_api/mastodon_api.ex | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..cb978c775 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -226,6 +226,12 @@ def follow_operation do type: :boolean, description: "Receive this account's reblogs in home timeline? Defaults to true.", default: true + }, + notify: %Schema{ + type: :boolean, + description: + "Receive notifications for all statuses posted by the account? Defaults to false.", + default: false } } }, diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 71479550e..fb713d47c 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -24,6 +24,7 @@ def follow(follower, followed, params \\ %{}) do with {:ok, follower, _followed, _} <- result do options = cast_params(params) set_reblogs_visibility(options[:reblogs], result) + set_subscription(options[:notify], result) {:ok, follower} end end @@ -36,6 +37,14 @@ defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do CommonAPI.show_reblogs(follower, followed) end + defp set_subscription(true, {:ok, follower, followed, _}) do + User.subscribe(follower, followed) + end + + defp set_subscription(_, {:ok, follower, followed, _}) do + User.unsubscribe(follower, followed) + end + @spec get_followers(User.t(), map()) :: list(User.t()) def get_followers(user, params \\ %{}) do user @@ -73,7 +82,8 @@ defp cast_params(params) do exclude_visibilities: {:array, :string}, reblogs: :boolean, with_muted: :boolean, - account_ap_id: :string + account_ap_id: :string, + notify: :boolean } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) From 3892bd353b68aff51fd596239de43fb320616eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 21:13:14 +0100 Subject: [PATCH 37/82] Add test for following with subscription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../controllers/account_controller_test.exs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index a92a58224..70c6b152e 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -922,6 +922,27 @@ test "following with reblogs" do |> json_response_and_validate_schema(200) end + test "following with subscription and unsubscribing when notify is nil" do + %{conn: conn} = oauth_access(["follow"]) + followed = insert(:user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true}) + + assert %{"id" => _id, "subscribing" => true} = + json_response_and_validate_schema(ret_conn, 200) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow") + + assert %{"id" => _id, "subscribing" => false} = + json_response_and_validate_schema(ret_conn, 200) + end + test "following / unfollowing errors", %{user: user, conn: conn} do # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") From c96e52b88c47371de1cc4ed70786baf20008a811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 21:23:34 +0100 Subject: [PATCH 38/82] Add 'notifying' to relationship for compatibility with Mastodon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- docs/development/API/pleroma_api.md | 2 ++ .../api_spec/operations/account_operation.ex | 3 +++ lib/pleroma/web/api_spec/schemas/account.ex | 3 ++- .../api_spec/schemas/account_relationship.ex | 6 ++++-- lib/pleroma/web/api_spec/schemas/status.ex | 3 ++- lib/pleroma/web/mastodon_api/mastodon_api.ex | 4 +++- .../web/mastodon_api/views/account_view.ex | 19 +++++++++++-------- .../controllers/account_controller_test.exs | 4 ++-- .../mastodon_api/views/account_view_test.exs | 2 ++ 9 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 8f6422da0..74a1ad206 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -159,6 +159,7 @@ See [Admin-API](admin_api.md) "muting": false, "muting_notifications": false, "subscribing": true, + "notifying": true, "requested": false, "domain_blocking": false, "showing_reblogs": true, @@ -183,6 +184,7 @@ See [Admin-API](admin_api.md) "muting": false, "muting_notifications": false, "subscribing": false, + "notifying": false, "requested": false, "domain_blocking": false, "showing_reblogs": true, diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index cb978c775..4fe5a3c03 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -694,6 +694,7 @@ defp array_of_relationships do "requested" => false, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => true }, %{ @@ -708,6 +709,7 @@ defp array_of_relationships do "requested" => true, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => false }, %{ @@ -722,6 +724,7 @@ defp array_of_relationships do "requested" => false, "domain_blocking" => true, "subscribing" => true, + "notifying" => true, "endorsed" => false } ] diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..ad1a85544 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -196,7 +196,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "settings_store" => %{ "pleroma-fe" => %{} diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..b4f6d25b0 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -24,7 +24,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do muting_notifications: %Schema{type: :boolean}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, - subscribing: %Schema{type: :boolean} + subscribing: %Schema{type: :boolean}, + notifying: %Schema{type: :boolean} }, example: %{ "blocked_by" => false, @@ -38,7 +39,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false } }) end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..0bf3312d1 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -284,7 +284,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "skip_thread_containment" => false, "tags" => [] diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index fb713d47c..23846b36a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -41,10 +41,12 @@ defp set_subscription(true, {:ok, follower, followed, _}) do User.subscribe(follower, followed) end - defp set_subscription(_, {:ok, follower, followed, _}) do + defp set_subscription(false, {:ok, follower, followed, _}) do User.unsubscribe(follower, followed) end + defp set_subscription(_, _), do: {:ok, nil} + @spec get_followers(User.t(), map()) :: list(User.t()) def get_followers(user, params \\ %{}) do user diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..25752cf56 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -101,6 +101,15 @@ def render( User.following?(target, reading_user) end + subscribing = + UserRelationship.exists?( + user_relationships, + :inverse_subscription, + target, + reading_user, + &User.subscribed_to?(&2, &1) + ) + # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags %{ id: to_string(target.id), @@ -138,14 +147,8 @@ def render( target, &User.muted_notifications?(&1, &2) ), - subscribing: - UserRelationship.exists?( - user_relationships, - :inverse_subscription, - target, - reading_user, - &User.subscribed_to?(&2, &1) - ), + subscribing: subscribing, + notifying: subscribing, requested: follow_state == :follow_pending, domain_blocking: User.blocks_domain?(reading_user, target), showing_reblogs: diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 70c6b152e..581944b8a 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -922,7 +922,7 @@ test "following with reblogs" do |> json_response_and_validate_schema(200) end - test "following with subscription and unsubscribing when notify is nil" do + test "following with subscription and unsubscribing" do %{conn: conn} = oauth_access(["follow"]) followed = insert(:user) @@ -937,7 +937,7 @@ test "following with subscription and unsubscribing when notify is nil" do ret_conn = conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{followed.id}/follow") + |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false}) assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(ret_conn, 200) diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..aeddd6b4c 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -268,6 +268,7 @@ defp test_relationship_rendering(user, other_user, expected_result) do muting: false, muting_notifications: false, subscribing: false, + notifying: false, requested: false, domain_blocking: false, showing_reblogs: true, @@ -293,6 +294,7 @@ test "represent a relationship for the following and followed user" do muting: true, muting_notifications: true, subscribing: true, + notifying: true, showing_reblogs: false, id: to_string(other_user.id) } From dff435488d02a7d3aba09dca778a97e395f4e210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 12 Dec 2021 17:43:18 +0100 Subject: [PATCH 39/82] Add link headers in ChatController.index2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/pleroma_api/controllers/chat_controller.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index dcd54b1af..669d50132 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -151,7 +151,9 @@ def index2(%{assigns: %{user: user}} = conn, params) do index_query(user, params) |> Pagination.fetch_paginated(params) - render(conn, "index.json", chats: chats) + conn + |> add_link_headers(chats) + |> render("index.json", chats: chats) end defp index_query(%{id: user_id} = user, params) do From 108dfd1f87087e9bb61bffa310ddb67a58d5336a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 12 Dec 2021 22:50:07 -0600 Subject: [PATCH 40/82] Search: limit number of results --- lib/pleroma/web/mastodon_api/controllers/search_controller.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 64b177eb3..1459fc492 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do require Logger + @search_limit 40 + plug(Pleroma.Web.ApiSpec.CastAndValidate) # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) @@ -77,7 +79,7 @@ defp search_options(params, user) do [ resolve: params[:resolve], following: params[:following], - limit: params[:limit], + limit: min(params[:limit], @search_limit), offset: params[:offset], type: params[:type], author: get_author(params), From abb62dd8863a3fde0d329e2d529bca8346e9b177 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 15 Dec 2021 13:53:09 -0500 Subject: [PATCH 41/82] Application, dependencies: prepare for finch --- lib/pleroma/application.ex | 4 ++++ mix.exs | 5 ++--- mix.lock | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 9824e0a4a..34eaed181 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -61,6 +61,10 @@ def start(_type, _args) do adapter = Application.get_env(:tesla, :adapter) + if adapter == Tesla.Adapter.Finch do + Finch.start_link(name: MyFinch) + end + if adapter == Tesla.Adapter.Gun do if version = Pleroma.OTPVersion.version() do [major, minor] = diff --git a/mix.exs b/mix.exs index e69c737dd..581b07f67 100644 --- a/mix.exs +++ b/mix.exs @@ -137,6 +137,7 @@ defp deps do {:castore, "~> 0.1"}, {:cowlib, "~> 2.9", override: true}, {:gun, "~> 2.0.0-rc.1", override: true}, + {:finch, "~> 0.10.0"}, {:jason, "~> 1.2"}, {:mogrify, "~> 0.9.1"}, {:ex_aws, "~> 2.1.6"}, @@ -192,9 +193,7 @@ defp deps do git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, {:restarter, path: "./restarter"}, - {:majic, - git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", - ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"}, + {:majic, "~> 1.0"}, {:eblurhash, "~> 1.1.0"}, {:open_api_spex, "~> 3.10"}, diff --git a/mix.lock b/mix.lock index 18d5e3bea..f9646d7ac 100644 --- a/mix.lock +++ b/mix.lock @@ -45,9 +45,10 @@ "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, "ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, - "fast_html": {:hex, :fast_html, "2.0.4", "4910ee49f2f6b19692e3bf30bf97f1b6b7dac489cd6b0f34cd0fe3042c56ba30", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "3bb49d541dfc02ad5e425904f53376d758c09f89e521afc7d2b174b3227761ea"}, + "fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"}, "fast_sanitize": {:hex, :fast_sanitize, "0.2.2", "3cbbaebaea6043865dfb5b4ecb0f1af066ad410a51470e353714b10c42007b81", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "69f204db9250afa94a0d559d9110139850f57de2b081719fbafa1e9a89e94466"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "finch": {:hex, :finch, "0.10.0", "8e5e6101ae98e7f1ef830594f774411a2f9cbce4f92d8179502da69fbbff52bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80324ba22edbdebca6fac05c8517e7457b79dfe101e3bf6b2f7c5c65c93a9077"}, "flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"}, "floki": {:hex, :floki, "0.30.1", "75d35526d3a1459920b6e87fdbc2e0b8a3670f965dd0903708d2b267e0904c55", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e9c03524447d1c4cbfccd672d739b8c18453eee377846b119d4fd71b1a176bb8"}, "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"}, @@ -68,7 +69,7 @@ "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, "linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"}, - "majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "289cda1b6d0d70ccb2ba508a2b0bd24638db2880", [ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"]}, + "majic": {:hex, :majic, "1.0.0", "f493c28a9f38338b5f0abae4a9f31b6a9bdaffe8b1cc62742a7fedf9290dd182", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "03d7c48087da15039c5273bfb85da990b3fb08d2f541612fc9222dbae4bd7adc"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, @@ -76,13 +77,15 @@ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, "mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"}, "mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"}, "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]}, + "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"}, - "nimble_pool": {:hex, :nimble_pool, "0.1.0", "ffa9d5be27eee2b00b0c634eb649aa27f97b39186fec3c493716c2a33e784ec6", [:mix], [], "hexpm", "343a1eaa620ddcf3430a83f39f2af499fe2370390d4f785cd475b4df5acaf3f9"}, + "nimble_pool": {:hex, :nimble_pool, "0.2.4", "1db8e9f8a53d967d595e0b32a17030cdb6c0dc4a451b8ac787bf601d3f7704c3", [:mix], [], "hexpm", "367e8071e137b787764e6a9992ccb57b276dc2282535f767a07d881951ebeac6"}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"}, "open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"}, From 4e98ba3c3a96548fe6d7fa8705898c660b788fea Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 15 Dec 2021 15:42:37 -0500 Subject: [PATCH 42/82] Application: Actually start finch if it's needed --- lib/pleroma/application.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 34eaed181..952579c7f 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -61,7 +61,8 @@ def start(_type, _args) do adapter = Application.get_env(:tesla, :adapter) - if adapter == Tesla.Adapter.Finch do + if match?({Tesla.Adapter.Finch, _}, adapter) do + Logger.info("Starting Finch") Finch.start_link(name: MyFinch) end From 29d80b39f287ed2488a612280d41e9dd2e40a7cc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 15 Dec 2021 16:17:11 -0500 Subject: [PATCH 43/82] Add Phoenix LiveDashboard Co-authored-by: Egor Kislitsyn --- config/config.exs | 1 + lib/mix/tasks/pleroma/instance.ex | 2 ++ lib/pleroma/web/endpoint.ex | 1 + lib/pleroma/web/router.ex | 6 ++++++ mix.exs | 5 ++++- mix.lock | 7 ++++++- priv/templates/sample_config.eex | 1 + test/pleroma/web/plugs/frontend_static_plug_test.exs | 1 + 8 files changed, 22 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index b50c910b1..4a916abf5 100644 --- a/config/config.exs +++ b/config/config.exs @@ -139,6 +139,7 @@ ], protocol: "https", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", + live_view: [signing_salt: "U5ELgdEwTD3n1+D5s0rY0AMg8/y1STxZ3Zvsl3bWh+oBcGrYdil0rXqPMRd3Glcq"], signing_salt: "CqaoopA2", render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)], pubsub_server: Pleroma.PubSub, diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index da27a99d0..d98cb8e37 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -199,6 +199,7 @@ def run(["gen" | rest]) do secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) + lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) template_dir = Application.app_dir(:pleroma, "priv") <> "/templates" @@ -217,6 +218,7 @@ def run(["gen" | rest]) do secret: secret, jwt_secret: jwt_secret, signing_salt: signing_salt, + lv_signing_salt: lv_signing_salt, web_push_public_key: Base.url_encode64(web_push_public_key, padding: false), web_push_private_key: Base.url_encode64(web_push_private_key, padding: false), db_configurable?: db_configurable?, diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 8e274de88..75484fac5 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do alias Pleroma.Config socket("/socket", Pleroma.Web.UserSocket) + socket("/live", Phoenix.LiveView.Socket) plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index efca7078a..f1d5caddf 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.Router do use Pleroma.Web, :router + import Phoenix.LiveDashboard.Router pipeline :accepts_html do plug(:accepts, ["html"]) @@ -778,6 +779,11 @@ defmodule Pleroma.Web.Router do end end + scope "/" do + pipe_through([:pleroma_html, :authenticate, :require_admin]) + live_dashboard("/phoenix/live_dashboard") + end + # Test-only routes needed to test action dispatching and plug chain execution if Pleroma.Config.get(:env) == :test do @test_actions [ diff --git a/mix.exs b/mix.exs index 39c79c83b..bf1e1fdd3 100644 --- a/mix.exs +++ b/mix.exs @@ -79,6 +79,7 @@ def application do :comeonin, :quack, :fast_sanitize, + :os_mon, :ssl ], included_applications: [:ex_syslogger] @@ -129,7 +130,7 @@ defp deps do {:trailing_format_plug, "~> 0.0.7"}, {:fast_sanitize, "~> 0.2.0"}, {:html_entities, "~> 0.5", override: true}, - {:phoenix_html, "~> 2.14"}, + {:phoenix_html, "~> 3.1", override: true}, {:calendar, "~> 1.0"}, {:cachex, "~> 3.2"}, {:poison, "~> 3.0", override: true}, @@ -197,6 +198,8 @@ defp deps do ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"}, {:eblurhash, "~> 1.1.0"}, {:open_api_spex, "~> 3.10"}, + {:phoenix_live_dashboard, "~> 0.6.2"}, + {:ecto_psql_extras, "~> 0.6"}, # indirect dependency version override {:plug, "~> 1.10.4", override: true}, diff --git a/mix.lock b/mix.lock index b78ae0bc9..4ec108b6a 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,7 @@ "eblurhash": {:hex, :eblurhash, "1.1.0", "e10ccae762598507ebfacf0b645ed49520f2afa3e7e9943e73a91117dffce415", [:rebar3], [], "hexpm", "2e6b889d09fddd374e3c5ac57c486138768763264e99ac1074ae5fa7fc9ab51d"}, "ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, + "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"}, "ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"}, "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"}, "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, @@ -91,7 +92,9 @@ "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, "phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"}, - "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"}, + "phoenix_html": {:hex, :phoenix_html, "3.1.0", "0b499df05aad27160d697a9362f0e89fa0e24d3c7a9065c2bd9d38b4d1416c09", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0c0a98a2cefa63433657983a2a594c7dee5927e4391e0f1bfd3a151d1def33fc"}, + "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.2", "0769470265eb13af01b5001b29cb935f4710d6adaa1ffc18417a570a337a2f0f", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5bc6c6b38a2ca8b5020b442322fcee6afd5e641637a0b1fb059d4bd89bc58e7b"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.5", "63f52a6f9f6983f04e424586ff897c016ecc5e4f8d1e2c22c2887af1c57215d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c5586e6a3d4df71b8214c769d4f5eb8ece2b4001711a7ca0f97323c36958b0e3"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.3", "039435dd975f7e55953525b88f1d596f26c6141412584c16f4db109708a8ee68", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4a540cea32e05356541737033d666ee7fea7700eb2101bf76783adbfe06601cd"}, "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"}, @@ -117,7 +120,9 @@ "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, "swoosh": {:hex, :swoosh, "1.3.11", "34f79c57f19892b43bd2168de9ff5de478a721a26328ef59567aad4243e7a77b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f1e2a048db454f9982b9cf840f75e7399dd48be31ecc2a7dc10012a803b913af"}, "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"}, + "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, + "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "tesla": {:hex, :tesla, "1.4.1", "ff855f1cac121e0d16281b49e8f066c4a0d89965f98864515713878cca849ac8", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "95f5de35922c8c4b3945bee7406f66eb680b0955232f78f5fb7e853aa1ce201a"}, "timex": {:hex, :timex, "3.7.5", "3eca56e23bfa4e0848f0b0a29a92fa20af251a975116c6d504966e8a90516dfd", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a15608dca680f2ef663d71c95842c67f0af08a0f3b1d00e17bbd22872e2874e4"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, diff --git a/priv/templates/sample_config.eex b/priv/templates/sample_config.eex index 42f496ded..0068969ac 100644 --- a/priv/templates/sample_config.eex +++ b/priv/templates/sample_config.eex @@ -13,6 +13,7 @@ config :pleroma, Pleroma.Web.Endpoint, url: [host: "<%= domain %>", scheme: "https", port: <%= port %>], http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>], secret_key_base: "<%= secret %>", + live_view: [signing_salt: "<%= lv_signing_salt %>"], signing_salt: "<%= signing_salt %>" config :pleroma, :instance, diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 4152cdefe..18103fe4c 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -98,6 +98,7 @@ test "api routes are detected correctly" do "auth", "embed", "proxy", + "phoenix", "test", "user_exists", "check_password" From 5660bee2dcfd200c726c7d7ce40b1f6b8d5048f2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 16 Dec 2021 11:36:58 -0600 Subject: [PATCH 44/82] Dirty hack to make mediaproxy functional by relying on Hackney for that part --- lib/pleroma/reverse_proxy/client/wrapper.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/reverse_proxy/client/wrapper.ex b/lib/pleroma/reverse_proxy/client/wrapper.ex index 06dd29fea..ce144559f 100644 --- a/lib/pleroma/reverse_proxy/client/wrapper.ex +++ b/lib/pleroma/reverse_proxy/client/wrapper.ex @@ -25,5 +25,6 @@ defp client do defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla + defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client) end From 31b9034a2775d108ba5f73ebb7ee0eb598177105 Mon Sep 17 00:00:00 2001 From: a1batross Date: Fri, 17 Dec 2021 14:15:44 +0000 Subject: [PATCH 45/82] emoji/loader.ex: be more verbose about which emoji pack config is loading now To avoid issue when one of the hundred JSON files is malformed and administrator don't know which one --- lib/pleroma/emoji/loader.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex index 95937a892..abc95d902 100644 --- a/lib/pleroma/emoji/loader.ex +++ b/lib/pleroma/emoji/loader.ex @@ -103,6 +103,7 @@ defp load_pack(pack_dir, emoji_groups) do pack_file = Path.join(pack_dir, "pack.json") if File.exists?(pack_file) do + Logger.info("Loading emoji pack from JSON: #{pack_file}") contents = Jason.decode!(File.read!(pack_file)) contents["files"] @@ -115,6 +116,7 @@ defp load_pack(pack_dir, emoji_groups) do emoji_txt = Path.join(pack_dir, "emoji.txt") if File.exists?(emoji_txt) do + Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}") load_from_file(emoji_txt, emoji_groups) else extensions = Config.get([:emoji, :pack_extensions]) From 3d41ccc47bd59cb17e7c18a368e3da3fd885ff29 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 14:17:51 -0500 Subject: [PATCH 46/82] Allow updating accepted follow activities in Web.ActivityPub.Utils.update_follow_state_for_all/2 Mastodon uses the Reject activity also for the purpose of removing a follower, in addition to reject a follow request. We should also update the original Follow activity in this case. --- lib/pleroma/web/activity_pub/utils.ex | 2 +- test/pleroma/web/activity_pub/utils_test.exs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 1df53f79a..c1f6b2b49 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -446,7 +446,7 @@ def update_follow_state_for_all( |> Activity.Queries.by_type() |> Activity.Queries.by_actor(actor) |> Activity.Queries.by_object_id(object) - |> where(fragment("data->>'state' = 'pending'")) + |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'")) |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)]) |> Repo.update_all([]) diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index ee3e1014e..62dc02f61 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -213,6 +213,20 @@ test "updates the state of all Follow activities with the same actor and object" assert refresh_record(follow_activity).data["state"] == "accept" assert refresh_record(follow_activity_two).data["state"] == "accept" end + + test "also updates the state of accepted follows" do + user = insert(:user) + follower = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + + {:ok, follow_activity_two} = + Utils.update_follow_state_for_all(follow_activity_two, "reject") + + assert refresh_record(follow_activity).data["state"] == "reject" + assert refresh_record(follow_activity_two).data["state"] == "reject" + end end describe "update_follow_state/2" do From bfd870380c6dca1c3d460991181438a02c4915f9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 14:42:45 -0500 Subject: [PATCH 47/82] Add test to ensure the blocked cease to have follow relationship to the blocker https://git.pleroma.social/pleroma/pleroma/-/issues/2766 --- test/pleroma/web/activity_pub/side_effects_test.exs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index d0988619d..5ca68ccc8 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -88,6 +88,16 @@ test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do assert User.blocks?(user, blocked) end + test "it updates following relationship", %{user: user, blocked: blocked, block: block} do + {:ok, _, _} = SideEffects.handle(block) + + refute Pleroma.FollowingRelationship.get(user, blocked) + assert User.get_follow_state(user, blocked) == nil + assert User.get_follow_state(blocked, user) == nil + assert User.get_follow_state(user, blocked, nil) == nil + assert User.get_follow_state(blocked, user, nil) == nil + end + test "it blocks but does not unfollow if the relevant setting is set", %{ user: user, blocked: blocked, From 951d1592c7958c2225a868360455693dd36def96 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:44:22 -0500 Subject: [PATCH 48/82] Add test to ensure removed follower cease to have relationship with ex-followee https://git.pleroma.social/pleroma/pleroma/-/issues/2802 --- .../web/activity_pub/side_effects_test.exs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index 5ca68ccc8..30dd63d4d 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -552,4 +552,71 @@ test "it streams out the announce", %{announce: announce} do end end end + + describe "removing a follower" do + setup do + user = insert(:user) + followed = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed) + + {:ok, reject_data, []} = Builder.reject(followed, follow_activity) + {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true) + + %{user: user, followed: followed, reject: reject} + end + + test "", %{user: user, followed: followed, reject: reject} do + assert User.following?(user, followed) + assert Pleroma.FollowingRelationship.get(user, followed) + + {:ok, _, _} = SideEffects.handle(reject) + + refute User.following?(user, followed) + refute Pleroma.FollowingRelationship.get(user, followed) + assert User.get_follow_state(user, followed) == nil + assert User.get_follow_state(user, followed, nil) == nil + end + end + + describe "removing a follower from remote" do + setup do + user = insert(:user) + followed = insert(:user, local: false) + + # Mock a local-to-remote follow + {:ok, follow_data, []} = Builder.follow(user, followed) + follow_data = + follow_data + |> Map.put("state", "accept") + {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true) + {:ok, _, _} = SideEffects.handle(follow) + + # Mock a remote-to-local accept + {:ok, accept_data, _} = Builder.accept(followed, follow) + {:ok, accept, _} = ActivityPub.persist(accept_data, local: false) + {:ok, _, _} = SideEffects.handle(accept) + + # Mock a remote-to-local reject + {:ok, reject_data, []} = Builder.reject(followed, follow) + {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false) + + %{user: user, followed: followed, reject: reject} + end + + test "", %{user: user, followed: followed, reject: reject} do + assert User.following?(user, followed) + assert Pleroma.FollowingRelationship.get(user, followed) + + {:ok, _, _} = SideEffects.handle(reject) + + refute User.following?(user, followed) + refute Pleroma.FollowingRelationship.get(user, followed) + + assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject" + + assert User.get_follow_state(user, followed) == nil + assert User.get_follow_state(user, followed, nil) == nil + end + end end From 538d5ac2100aac57814fbc11bb205be7bb205b96 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:47:48 -0500 Subject: [PATCH 49/82] Add changelog for https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3568 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecefba381..a3034c53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies +- Handle Reject for already-accepted Follows properly ### Removed From 8376e83f61b8dbe61134e814e093e8fe7288440f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:52:50 -0500 Subject: [PATCH 50/82] Lint --- test/pleroma/web/activity_pub/side_effects_test.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index 30dd63d4d..c6155ed18 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -586,9 +586,11 @@ test "", %{user: user, followed: followed, reject: reject} do # Mock a local-to-remote follow {:ok, follow_data, []} = Builder.follow(user, followed) + follow_data = follow_data |> Map.put("state", "accept") + {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true) {:ok, _, _} = SideEffects.handle(follow) @@ -613,7 +615,8 @@ test "", %{user: user, followed: followed, reject: reject} do refute User.following?(user, followed) refute Pleroma.FollowingRelationship.get(user, followed) - assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject" + assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == + "reject" assert User.get_follow_state(user, followed) == nil assert User.get_follow_state(user, followed, nil) == nil From ff17884c3b78fbae60e32bbad5503d93129ed76a Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 18:00:29 -0500 Subject: [PATCH 51/82] Bump alpine to 3.14 --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index db1a6b457..c51ebbab0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev &&\ mkdir release &&\ mix release --path release -FROM alpine:3.11 +FROM alpine:3.14 ARG BUILD_DATE ARG VCS_REF @@ -31,8 +31,7 @@ LABEL maintainer="ops@pleroma.social" \ ARG HOME=/opt/pleroma ARG DATA=/var/lib/pleroma -RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\ - apk update &&\ +RUN apk update &&\ apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\ adduser --system --shell /bin/false --home ${HOME} pleroma &&\ mkdir -p ${DATA}/uploads &&\ From e009950845c6d1e7864bb68ea1258c58438ee3aa Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 19 Dec 2021 20:35:00 +0300 Subject: [PATCH 52/82] Slow queries logging improvements: added EXPLAIN results, listed params, improved stacktrace. --- lib/pleroma/telemetry/logger.ex | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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 @@ 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, + %{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 @@ def handle_event( Logger.warn(fn -> """ - Query took longer than 500ms! + Slow query! Total time: #{query_time / 1_000}ms - #{inspect(query)} + #{query} - #{inspect(stacktrace, pretty: true)} + #{inspect(query_params)} + + #{sql_explain} + + #{Exception.format_stacktrace(pleroma_stacktrace)} """ end) end From 2ce7dae6de793f62b1e8e50492615dc28b9ab6fc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 Dec 2021 22:04:15 -0600 Subject: [PATCH 53/82] Skip erratic tests --- test/pleroma/config/transfer_task_test.exs | 4 ++++ test/pleroma/web/plugs/rate_limiter_test.exs | 4 ++++ test/test_helper.exs | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/pleroma/config/transfer_task_test.exs b/test/pleroma/config/transfer_task_test.exs index 7d51fd84c..9e3f11f1a 100644 --- a/test/pleroma/config/transfer_task_test.exs +++ b/test/pleroma/config/transfer_task_test.exs @@ -82,6 +82,7 @@ test "transfer config values with full subkey update" do on_exit(fn -> Restarter.Pleroma.refresh() end) end + @tag :erratic test "don't restart if no reboot time settings were changed" do clear_config(:emoji) insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) @@ -92,18 +93,21 @@ test "don't restart if no reboot time settings were changed" do ) end + @tag :erratic test "on reboot time key" do clear_config(:shout) insert(:config, key: :shout, value: [enabled: false]) assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" end + @tag :erratic test "on reboot time subkey" do clear_config(Pleroma.Captcha) insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" end + @tag :erratic test "don't restart pleroma on reboot time key and subkey if there is false flag" do clear_config(:shout) clear_config(Pleroma.Captcha) diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs index d007e3f26..b7cfde1f7 100644 --- a/test/pleroma/web/plugs/rate_limiter_test.exs +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -48,6 +48,7 @@ test "it is enabled if remote_ip_found flag doesn't exist" do refute RateLimiter.disabled?(build_conn()) end + @tag :erratic test "it restricts based on config values" do limiter_name = :test_plug_opts scale = 80 @@ -137,6 +138,7 @@ test "it supports combination of options modifying bucket name" do end describe "unauthenticated users" do + @tag :erratic test "are restricted based on remote IP" do limiter_name = :test_unauthenticated clear_config([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) @@ -174,6 +176,7 @@ test "are restricted based on remote IP" do :ok end + @tag :erratic test "can have limits separate from unauthenticated connections" do limiter_name = :test_authenticated1 @@ -199,6 +202,7 @@ test "can have limits separate from unauthenticated connections" do assert conn.halted end + @tag :erratic test "different users are counted independently" do limiter_name = :test_authenticated2 clear_config([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) diff --git a/test/test_helper.exs b/test/test_helper.exs index 0c9783076..9fb41e985 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: [] -ExUnit.start(exclude: [:federated | os_exclude]) +ExUnit.start(exclude: [:federated, :erratic] ++ os_exclude) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual) From 87871ac85722b0cec067decf698bf02fbf36dc93 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Thu, 23 Dec 2021 16:01:02 +0000 Subject: [PATCH 54/82] Add initial Nodeinfo document --- docs/development/API/nodeinfo.md | 347 +++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 docs/development/API/nodeinfo.md diff --git a/docs/development/API/nodeinfo.md b/docs/development/API/nodeinfo.md new file mode 100644 index 000000000..0f998a1e6 --- /dev/null +++ b/docs/development/API/nodeinfo.md @@ -0,0 +1,347 @@ +# Nodeinfo + +See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/). + +## `/.well-known/nodeinfo` +### The well-known path +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "links":[ + { + "href":"https://example.com/nodeinfo/2.0.json", + "rel":"http://nodeinfo.diaspora.software/ns/schema/2.0" + }, + { + "href":"https://example.com/nodeinfo/2.1.json", + "rel":"http://nodeinfo.diaspora.software/ns/schema/2.1" + } + ] +} +``` + +## `/nodeinfo/2.0.json` +### Nodeinfo 2.0 +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "metadata":{ + "accountActivationRequired":false, + "features":[ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma:api/v1/notifications:include_types_filter", + "chat", + "shout", + "relay", + "pleroma_emoji_reactions", + "pleroma_chat_messages" + ], + "federation":{ + "enabled":true, + "exclusions":false, + "mrf_hashtag":{ + "federated_timeline_removal":[ + + ], + "reject":[ + + ], + "sensitive":[ + "nsfw" + ] + }, + "mrf_object_age":{ + "actions":[ + "delist", + "strip_followers" + ], + "threshold":604800 + }, + "mrf_policies":[ + "ObjectAgePolicy", + "TagPolicy", + "HashtagPolicy" + ], + "quarantined_instances":[ + + ] + }, + "fieldsLimits":{ + "maxFields":10, + "maxRemoteFields":20, + "nameLength":512, + "valueLength":2048 + }, + "invitesEnabled":false, + "mailerEnabled":false, + "nodeDescription":"Pleroma: An efficient and flexible fediverse server", + "nodeName":"Example", + "pollLimits":{ + "max_expiration":31536000, + "max_option_chars":200, + "max_options":20, + "min_expiration":0 + }, + "postFormats":[ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode" + ], + "private":false, + "restrictedNicknames":[ + ".well-known", + "~", + "about", + "activities", + "api", + "auth", + "check_password", + "dev", + "friend-requests", + "inbox", + "internal", + "main", + "media", + "nodeinfo", + "notice", + "oauth", + "objects", + "ostatus_subscribe", + "pleroma", + "proxy", + "push", + "registration", + "relay", + "settings", + "status", + "tag", + "user-search", + "user_exists", + "users", + "web", + "verify_credentials", + "update_credentials", + "relationships", + "search", + "confirmation_resend", + "mfa" + ], + "skipThreadContainment":true, + "staffAccounts":[ + "https://example.com/users/admin", + "https://example.com/users/staff" + ], + "suggestions":{ + "enabled":false + }, + "uploadLimits":{ + "avatar":2000000, + "background":4000000, + "banner":4000000, + "general":16000000 + } + }, + "openRegistrations":true, + "protocols":[ + "activitypub" + ], + "services":{ + "inbound":[ + + ], + "outbound":[ + + ] + }, + "software":{ + "name":"pleroma", + "version":"2.4.1" + }, + "usage":{ + "localPosts":27, + "users":{ + "activeHalfyear":129, + "activeMonth":70, + "total":235 + } + }, + "version":"2.0" +} +``` + +## `/nodeinfo/2.1.json` +### Nodeinfo 2.1 +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "metadata":{ + "accountActivationRequired":false, + "features":[ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma:api/v1/notifications:include_types_filter", + "chat", + "shout", + "relay", + "pleroma_emoji_reactions", + "pleroma_chat_messages" + ], + "federation":{ + "enabled":true, + "exclusions":false, + "mrf_hashtag":{ + "federated_timeline_removal":[ + + ], + "reject":[ + + ], + "sensitive":[ + "nsfw" + ] + }, + "mrf_object_age":{ + "actions":[ + "delist", + "strip_followers" + ], + "threshold":604800 + }, + "mrf_policies":[ + "ObjectAgePolicy", + "TagPolicy", + "HashtagPolicy" + ], + "quarantined_instances":[ + + ] + }, + "fieldsLimits":{ + "maxFields":10, + "maxRemoteFields":20, + "nameLength":512, + "valueLength":2048 + }, + "invitesEnabled":false, + "mailerEnabled":false, + "nodeDescription":"Pleroma: An efficient and flexible fediverse server", + "nodeName":"Example", + "pollLimits":{ + "max_expiration":31536000, + "max_option_chars":200, + "max_options":20, + "min_expiration":0 + }, + "postFormats":[ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode" + ], + "private":false, + "restrictedNicknames":[ + ".well-known", + "~", + "about", + "activities", + "api", + "auth", + "check_password", + "dev", + "friend-requests", + "inbox", + "internal", + "main", + "media", + "nodeinfo", + "notice", + "oauth", + "objects", + "ostatus_subscribe", + "pleroma", + "proxy", + "push", + "registration", + "relay", + "settings", + "status", + "tag", + "user-search", + "user_exists", + "users", + "web", + "verify_credentials", + "update_credentials", + "relationships", + "search", + "confirmation_resend", + "mfa" + ], + "skipThreadContainment":true, + "staffAccounts":[ + "https://example.com/users/admin", + "https://example.com/users/staff" + ], + "suggestions":{ + "enabled":false + }, + "uploadLimits":{ + "avatar":2000000, + "background":4000000, + "banner":4000000, + "general":16000000 + } + }, + "openRegistrations":true, + "protocols":[ + "activitypub" + ], + "services":{ + "inbound":[ + + ], + "outbound":[ + + ] + }, + "software":{ + "name":"pleroma", + "repository":"https://git.pleroma.social/pleroma/pleroma", + "version":"2.4.1" + }, + "usage":{ + "localPosts":27, + "users":{ + "activeHalfyear":129, + "activeMonth":70, + "total":235 + } + }, + "version":"2.1" +} +``` + From 2c06eff519f63e67aada70d492094e6e56bbfccd Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 25 Dec 2021 20:11:14 -0600 Subject: [PATCH 55/82] Pleroma.Web.base_url() --> Endpoint.url() --- test/pleroma/web/o_status/o_status_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index b243e1692..41aef98b1 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -356,7 +356,7 @@ test "Soapbox FE", %{conn: conn} do |> response(200) expected = - "" + "" assert resp =~ expected end @@ -372,7 +372,7 @@ test "Mastodon", %{conn: conn} do |> response(200) expected = - "" + "" assert resp =~ expected end @@ -388,7 +388,7 @@ test "Twitter", %{conn: conn} do |> response(200) expected = - "" + "" assert resp =~ expected end From cac4ed5eb0b860acb077baefcc2e3b3447c89751 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 25 Dec 2021 20:15:21 -0600 Subject: [PATCH 56/82] GitLab CI: don't retry failed jobs --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 844f5888e..f296f7bd9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,7 +79,6 @@ unit-testing: - "**/*.ex" - "**/*.exs" - "mix.lock" - retry: 2 cache: &testing_cache_policy <<: *global_cache_policy policy: pull @@ -117,7 +116,6 @@ unit-testing-rum: - "**/*.ex" - "**/*.exs" - "mix.lock" - retry: 2 cache: *testing_cache_policy services: - name: minibikini/postgres-with-rum:12 From de006443f0bc8cfb3ad28b29b2d8ea9581e760b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 26 Dec 2021 02:35:17 +0000 Subject: [PATCH 57/82] MastoAPI: Profile directory --- config/config.exs | 3 +- config/description.exs | 5 ++ .../API/differences_in_mastoapi_responses.md | 6 -- lib/pleroma/user.ex | 13 +++ lib/pleroma/user/query.ex | 5 ++ lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++ lib/pleroma/web/activity_pub/side_effects.ex | 1 + .../operations/directory_operation.ex | 41 ++++++++++ .../controllers/directory_controller.ex | 82 +++++++++++++++++++ .../web/mastodon_api/views/account_view.ex | 1 + .../web/mastodon_api/views/instance_view.ex | 3 + lib/pleroma/web/router.ex | 2 + ...1222165256_add_last_status_at_to_users.exs | 11 +++ ...802_add_is_discoverable_index_to_users.exs | 7 ++ .../controllers/directory_controller_test.exs | 46 +++++++++++ .../mastodon_api/views/account_view_test.exs | 2 + 16 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/directory_operation.ex create mode 100644 lib/pleroma/web/mastodon_api/controllers/directory_controller.ex create mode 100644 priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs create mode 100644 priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs diff --git a/config/config.exs b/config/config.exs index c9592511f..23c41eddd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -254,7 +254,8 @@ ] ], show_reactions: true, - password_reset_token_validity: 60 * 60 * 24 + password_reset_token_validity: 60 * 60 * 24, + profile_directory: true config :pleroma, :welcome, direct_message: [ diff --git a/config/description.exs b/config/description.exs index 1c8c3b4a0..517077acf 100644 --- a/config/description.exs +++ b/config/description.exs @@ -936,6 +936,11 @@ key: :show_reactions, type: :boolean, description: "Let favourites and emoji reactions be viewed through the API." + }, + %{ + key: :profile_directory, + type: :boolean, + description: "Enable profile directory." } ] }, diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md index 6c1ecb559..518aca114 100644 --- a/docs/development/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -383,12 +383,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat - `GET /api/v1/endorsements`: Returns an empty array, `[]` -### Profile directory - -*Added in Mastodon 3.0.0* - -- `GET /api/v1/directory`: Returns HTTP 404 - ### Featured tags *Added in Mastodon 3.0.0* diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c25023dc1..390de1e2d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -149,6 +149,7 @@ defmodule Pleroma.User do field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) field(:is_suggested, :boolean, default: false) + field(:last_status_at, :naive_datetime) embeds_one( :notification_settings, @@ -2499,4 +2500,16 @@ def active_user_count(days \\ 30) do |> where([u], u.local == true) |> Repo.aggregate(:count) end + + def update_last_status_at(user) do + User + |> where(id: ^user.id) + |> update([u], set: [last_status_at: fragment("NOW()")]) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 6d4a4ead6..bf78cb32d 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -47,6 +47,7 @@ defmodule Pleroma.User.Query do is_admin: boolean(), is_moderator: boolean(), is_suggested: boolean(), + is_discoverable: boolean(), super_users: boolean(), invisible: boolean(), internal: boolean(), @@ -172,6 +173,10 @@ defp compose_query({:is_suggested, bool}, query) do where(query, [u], u.is_suggested == ^bool) end + defp compose_query({:is_discoverable, bool}, query) do + where(query, [u], u.is_discoverable == ^bool) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8324ca22c..756096952 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -81,6 +81,10 @@ def decrease_note_count_if_public(actor, object) do if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end + def update_last_status_at_if_public(actor, object) do + if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} + end + defp increase_replies_count_if_reply(%{ "object" => %{"inReplyTo" => reply_ap_id} = object, "type" => "Create" @@ -288,6 +292,7 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param _ <- increase_replies_count_if_reply(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), + {:ok, _actor} <- update_last_status_at_if_public(actor, activity), _ <- notify_and_stream(activity), :ok <- maybe_schedule_poll_notifications(activity), :ok <- maybe_federate(activity) do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index d55a4b340..39d37fbcb 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -199,6 +199,7 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) + {:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object) if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex new file mode 100644 index 000000000..9be965feb --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.DirectoryOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Directory"], + summary: "Profile directory", + operationId: "DirectoryController.index", + parameters: + [ + Operation.parameter( + :order, + :query, + :string, + "Order by recent activity or account creation", + required: nil + ), + Operation.parameter(:local, :query, BooleanLike, "Include local users only") + ] ++ pagination_params(), + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex new file mode 100644 index 000000000..45ef227fb --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DirectoryController do + use Pleroma.Web, :controller + + import Ecto.Query + alias Pleroma.Pagination + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.MastodonAPI.AccountView + + require Logger + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug(:skip_auth when action == "index") + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation + + @doc "GET /api/v1/directory" + def index(%{assigns: %{user: user}} = conn, params) do + with true <- Pleroma.Config.get([:instance, :profile_directory]) do + limit = Map.get(params, :limit, 20) |> min(80) + + users = + User.Query.build(%{is_discoverable: true, invisible: false, limit: limit}) + |> order_by_creation_date(params) + |> exclude_remote(params) + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute]) + |> Pagination.fetch_paginated(params, :offset) + + conn + |> put_view(AccountView) + |> render("index.json", for: user, users: users, as: :user) + else + _ -> json(conn, []) + end + end + + defp order_by_creation_date(query, %{order: "new"}) do + query + end + + defp order_by_creation_date(query, _params) do + query + |> order_by([u], desc_nulls_last: u.last_status_at) + end + + defp exclude_remote(query, %{local: true}) do + where(query, [u], u.local == true) + end + + defp exclude_remote(query, _params) do + query + end + + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_user(query, _user) do + query + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_relationships(query, _user, _relationship_types) do + query + end +end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 3c8dd0353..4b15b1635 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -270,6 +270,7 @@ defp do_render("show.json", %{user: user} = opts) do actor_type: user.actor_type } }, + last_status_at: user.last_status_at, # Pleroma extensions # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index ec7d150a9..7072d5d61 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -87,6 +87,9 @@ def features do "pleroma_chat_messages", if Config.get([:instance, :show_reactions]) do "exposable_reactions" + end, + if Config.get([:instance, :profile_directory]) do + "profile_directory" end ] |> Enum.filter(& &1) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9ce35ad6b..e3659b87a 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -600,6 +600,8 @@ defmodule Pleroma.Web.Router do get("/timelines/tag/:tag", TimelineController, :hashtag) get("/polls/:id", PollController, :show) + + get("/directory", DirectoryController, :index) end scope "/api/v2", Pleroma.Web.MastodonAPI do diff --git a/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs b/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs new file mode 100644 index 000000000..906178216 --- /dev/null +++ b/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddLastStatusAtToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:last_status_at, :naive_datetime) + end + + create_if_not_exists(index(:users, [:last_status_at])) + end +end diff --git a/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs b/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs new file mode 100644 index 000000000..9f8f52b65 --- /dev/null +++ b/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.AddIsDiscoverableIndexToUsers do + use Ecto.Migration + + def change do + create(index(:users, [:is_discoverable])) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs new file mode 100644 index 000000000..b8f55f832 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs @@ -0,0 +1,46 @@ +defmodule Pleroma.Web.MastodonAPI.DirectoryControllerTest do + use Pleroma.Web.ConnCase, async: true + alias Pleroma.Web.CommonAPI + import Pleroma.Factory + + test "GET /api/v1/directory with :profile_directory disabled returns empty array", %{conn: conn} do + clear_config([:instance, :profile_directory], false) + + insert(:user, is_discoverable: true) + insert(:user, is_discoverable: true) + + result = + conn + |> get("/api/v1/directory") + |> json_response_and_validate_schema(200) + + assert result == [] + end + + test "GET /api/v1/directory returns discoverable users only", %{conn: conn} do + %{id: user_id} = insert(:user, is_discoverable: true) + insert(:user, is_discoverable: false) + + result = + conn + |> get("/api/v1/directory") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^user_id}] = result + end + + test "GET /api/v1/directory returns users sorted by most recent statuses", %{conn: conn} do + insert(:user, is_discoverable: true) + %{id: user_id} = user = insert(:user, is_discoverable: true) + insert(:user, is_discoverable: true) + + {:ok, _activity} = CommonAPI.post(user, %{status: "yay i'm discoverable"}) + + result = + conn + |> get("/api/v1/directory?order=active") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^user_id} | _tail] = result + end +end diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 39b9b0cef..c23ffb966 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -74,6 +74,7 @@ test "Represent a user account" do fields: [] }, fqn: "shp@shitposter.club", + last_status_at: nil, pleroma: %{ ap_id: user.ap_id, also_known_as: ["https://shitposter.zone/users/shp"], @@ -175,6 +176,7 @@ test "Represent a Service(bot) account" do fields: [] }, fqn: "shp@shitposter.club", + last_status_at: nil, pleroma: %{ ap_id: user.ap_id, also_known_as: [], From e8e8d2262ec55acabb7f7749f915e24d06df601a Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 26 Dec 2021 16:14:56 +0100 Subject: [PATCH 58/82] CI: Start testing erratic test again Erratic tests are now ran in their own task, so we don't block normal testing. The runtime is under a minute, so even if this one has to be retried, it shouldn't take forever. --- .gitlab-ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f296f7bd9..3860f1db9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -93,6 +93,27 @@ unit-testing: - mix ecto.migrate - mix coveralls --preload-modules +unit-testing-erratic: + stage: test + retry: 2 + only: + changes: + - "**/*.ex" + - "**/*.exs" + - "mix.lock" + cache: &testing_cache_policy + <<: *global_cache_policy + policy: pull + + services: + - name: postgres:13 + alias: postgres + command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] + script: + - mix ecto.create + - mix ecto.migrate + - mix test --only=erratic + # Removed to fix CI issue. In this early state it wasn't adding much value anyway. # TODO Fix and reinstate federated testing # federated-testing: From 7ed22589798f7282f12d6b7026da940d14a9351e Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 26 Dec 2021 17:00:09 +0100 Subject: [PATCH 59/82] Update changelog --- CHANGELOG.md | 1 + mix.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee9e04568..18f6b1c81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object +- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies diff --git a/mix.lock b/mix.lock index e9b1ed619..f371a6e41 100644 --- a/mix.lock +++ b/mix.lock @@ -70,7 +70,7 @@ "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, "linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"}, - "majic": {:hex, :majic, "1.0.0", "f493c28a9f38338b5f0abae4a9f31b6a9bdaffe8b1cc62742a7fedf9290dd182", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "03d7c48087da15039c5273bfb85da990b3fb08d2f541612fc9222dbae4bd7adc"}, + "majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, From c52390a7d9170c00371561115f3eb8eea6fb3515 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 26 Dec 2021 18:05:42 +0100 Subject: [PATCH 60/82] CI: Use own package as base So we can skip updating and installing the same packages a million times. It will still grab the hex.pm stuff -- maybe we can find a way to avoid this, too. --- .gitlab-ci.yml | 9 +-------- ci/Dockerfile | 7 +++++++ ci/build_and_push.sh | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 ci/Dockerfile create mode 100755 ci/build_and_push.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3860f1db9..9155af0e5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: elixir:1.9.4 +image: git.pleroma.social:5050/pleroma/pleroma/ci-base variables: &global_variables POSTGRES_DB: pleroma_test @@ -26,12 +26,7 @@ stages: before_script: - echo $MIX_ENV - rm -rf _build/*/lib/pleroma - - apt-get update && apt-get install -y cmake - - mix local.hex --force - - mix local.rebar --force - mix deps.get - - apt-get -qq update - - apt-get install -y libmagic-dev after_script: - rm -rf _build/*/lib/pleroma @@ -88,7 +83,6 @@ unit-testing: alias: postgres command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] script: - - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - mix ecto.create - mix ecto.migrate - mix coveralls --preload-modules @@ -146,7 +140,6 @@ unit-testing-rum: <<: *global_variables RUM_ENABLED: "true" script: - - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - mix ecto.create - mix ecto.migrate - "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/" diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 000000000..e6a8b438c --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,7 @@ +FROM elixir:1.9.4 + +RUN apt-get update &&\ + apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ + mix local.hex --force &&\ + mix local.rebar --force + diff --git a/ci/build_and_push.sh b/ci/build_and_push.sh new file mode 100755 index 000000000..484cc2643 --- /dev/null +++ b/ci/build_and_push.sh @@ -0,0 +1 @@ +docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:latest --push . From ac3b5037219f75c0dcec424052dab2e2e65149bd Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 26 Dec 2021 18:54:54 +0100 Subject: [PATCH 61/82] CI: Fix the broken tasks. --- .gitlab-ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9155af0e5..bebd97efb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -154,6 +154,10 @@ lint: - "**/*.exs" - "mix.lock" cache: *testing_cache_policy + before_script: + - mix local.hex --force + - mix local.rebar --force + - mix deps.get script: - mix format --check-formatted @@ -177,8 +181,13 @@ cycles: - "**/*.exs" - "mix.lock" cache: {} - script: + before_script: + - mix local.hex --force + - mix local.rebar --force - mix deps.get + - apt-get update + - apt-get install cmake libmagic-dev -y + script: - mix compile - mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}' From 3e9e7178bc90754ad6f5414417079f6484b421e9 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 26 Dec 2021 22:49:00 +0300 Subject: [PATCH 62/82] 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. --- config/config.exs | 5 +++++ lib/pleroma/telemetry/logger.ex | 33 +++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/config/config.exs b/config/config.exs index b50c910b1..656778007 100644 --- a/config/config.exs +++ b/config/config.exs @@ -148,6 +148,8 @@ ] # Configures Elixir's Logger +config :logger, truncate: 65536 + config :logger, :console, level: :debug, format: "\n$time $metadata[$level] $message\n", @@ -852,6 +854,9 @@ {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]} ] +config :pleroma, :telemetry, + slow_queries_logging: [exclude_sources: [nil, "oban_jobs"], min_duration: 500_000] + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" 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 @@ 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, 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 @@ def handle_event( """ 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 @@ def handle_event( """ end) end - - def handle_event([:pleroma, :repo, :query], _measurements, _metadata, _config), do: :ok end From cd1041c3a413b9b3ba4c763308b5fd77a53d7c3c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:27:48 +0300 Subject: [PATCH 63/82] API: optionally restrict moderators from accessing sensitive data --- CHANGELOG.md | 3 +- config/config.exs | 3 +- config/description.exs | 5 +++ .../web/plugs/ensure_staff_privileged.ex | 31 +++++++++++++++++++ lib/pleroma/web/router.ex | 31 +++++++++++++------ 5 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 lib/pleroma/web/plugs/ensure_staff_privileged.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index ee9e04568..79fe674a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object +- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis +- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies @@ -67,7 +69,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Attachment dimensions and blurhashes are federated when available. - Mastodon API: support `poll` notification. - Pinned posts federation -- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis ### Fixed - Don't crash so hard when email settings are invalid. diff --git a/config/config.exs b/config/config.exs index 23c41eddd..ec242cadc 100644 --- a/config/config.exs +++ b/config/config.exs @@ -255,7 +255,8 @@ ], show_reactions: true, password_reset_token_validity: 60 * 60 * 24, - profile_directory: true + profile_directory: true, + privileged_staff: false config :pleroma, :welcome, direct_message: [ diff --git a/config/description.exs b/config/description.exs index 517077acf..a8fbd4d73 100644 --- a/config/description.exs +++ b/config/description.exs @@ -941,6 +941,11 @@ key: :profile_directory, type: :boolean, description: "Enable profile directory." + }, + %{ + key: :privileged_staff, + type: :boolean, + description: "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" } ] }, 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 From 1c223331fc7276a7e5946b6dbd5d2b713cd6c1e8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:28:09 +0300 Subject: [PATCH 64/82] 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(-) 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 @@ def render("show.json", _) 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 @@ def get_nodeinfo("2.0") 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 From f66675f349a6e6b8111280e1abd23871688f6179 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 02:57:54 +0300 Subject: [PATCH 65/82] API: fix duplicate :get_password_token route --- lib/pleroma/web/router.ex | 1 - 1 file changed, 1 deletion(-) 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) From f02715c4b2bfe5b1f055e44d8fece2047d85b611 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 27 Dec 2021 03:12:32 +0300 Subject: [PATCH 66/82] Fix lint errors --- config/description.exs | 3 ++- ...ure_staff_privileged.ex => ensure_staff_privileged_plug.ex} | 2 +- lib/pleroma/web/router.ex | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename lib/pleroma/web/plugs/{ensure_staff_privileged.ex => ensure_staff_privileged_plug.ex} (100%) diff --git a/config/description.exs b/config/description.exs index a8fbd4d73..ea3f34abe 100644 --- a/config/description.exs +++ b/config/description.exs @@ -945,7 +945,8 @@ %{ key: :privileged_staff, type: :boolean, - description: "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" + description: + "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" } ] }, diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged.ex b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex similarity index 100% rename from lib/pleroma/web/plugs/ensure_staff_privileged.ex rename to lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex index b15ddfc56..fe0a11dec 100644 --- a/lib/pleroma/web/plugs/ensure_staff_privileged.ex +++ b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex @@ -10,8 +10,8 @@ defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do import Pleroma.Web.TranslationHelpers import Plug.Conn - alias Pleroma.User alias Pleroma.Config + alias Pleroma.User def init(options) do options 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) From 08c0f09bad040ea713893be822342867f589efbe Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 27 Dec 2021 09:13:31 +0300 Subject: [PATCH 67/82] Made slow queries logging disabled by default. --- config/config.exs | 6 +++++- lib/pleroma/telemetry/logger.ex | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index 656778007..30113a2e3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -855,7 +855,11 @@ ] config :pleroma, :telemetry, - slow_queries_logging: [exclude_sources: [nil, "oban_jobs"], min_duration: 500_000] + slow_queries_logging: [ + enabled: false, + min_duration: 500_000, + exclude_sources: [nil, "oban_jobs"] + ] # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. 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 @@ def handle_event( ) 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) From 479fc5fff8355e552c5d2297d83f4ca7456d4f03 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 10:39:59 -0600 Subject: [PATCH 68/82] EnsureStaffPrivilegedPlug: add tests --- .../ensure_staff_privileged_plug_test.exs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs diff --git a/test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs b/test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs new file mode 100644 index 000000000..74f4ae504 --- /dev/null +++ b/test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug + import Pleroma.Factory + + test "accepts a user that is an admin" do + user = insert(:user, is_admin: true) + + conn = assign(build_conn(), :user, user) + + ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{}) + + assert conn == ret_conn + end + + test "accepts a user that is a moderator when :privileged_staff is enabled" do + clear_config([:instance, :privileged_staff], true) + user = insert(:user, is_moderator: true) + + conn = assign(build_conn(), :user, user) + + ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{}) + + assert conn == ret_conn + end + + test "denies a user that is a moderator when :privileged_staff is disabled" do + clear_config([:instance, :privileged_staff], false) + user = insert(:user, is_moderator: true) + + conn = + build_conn() + |> assign(:user, user) + |> EnsureStaffPrivilegedPlug.call(%{}) + + assert conn.status == 403 + end + + test "denies a user that isn't a staff member" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> EnsureStaffPrivilegedPlug.call(%{}) + + assert conn.status == 403 + end + + test "denies when a user isn't set" do + conn = EnsureStaffPrivilegedPlug.call(build_conn(), %{}) + + assert conn.status == 403 + end +end From d61a5515e6a7b22c226ee465578cbc5cccce18e4 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 11:27:25 -0600 Subject: [PATCH 69/82] ConnectionPoolTest: tag erratic test --- test/pleroma/gun/connection_pool_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pleroma/gun/connection_pool_test.exs b/test/pleroma/gun/connection_pool_test.exs index 4b3158625..51637f541 100644 --- a/test/pleroma/gun/connection_pool_test.exs +++ b/test/pleroma/gun/connection_pool_test.exs @@ -46,6 +46,7 @@ test "gives the same connection to 2 concurrent requests" do end end + @tag :erratic test "connection limit is respected with concurrent requests" do clear_config([:connections_pool, :max_connections]) do clear_config([:connections_pool, :max_connections], 1) From a3fa9876118942e134f7c50778b4c20f899e0df7 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 16:58:10 -0600 Subject: [PATCH 70/82] AdminAPI: fix duplicated routes --- lib/pleroma/web/router.ex | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 02ca8d70a..6defc8080 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -266,9 +266,6 @@ defmodule Pleroma.Web.Router do post("/users/revoke_invite", InviteController, :revoke) post("/users/email_invite", InviteController, :email) - patch("/users/force_password_reset", AdminAPIController, :force_password_reset) - get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) - get("/users", UserController, :index) get("/users/:nickname", UserController, :show) From 138f5a4517b7035597a4622a0dc293b6dec7a372 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 17:18:26 -0600 Subject: [PATCH 71/82] EnsureStaffPrivilegedPlug: don't let non-moderators through --- lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex index fe0a11dec..c6ed45635 100644 --- a/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex +++ b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex @@ -4,9 +4,8 @@ defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do @moduledoc """ - Ensures if staff are privileged enough to do certain tasks + Ensures staff are privileged enough to do certain tasks. """ - import Pleroma.Web.TranslationHelpers import Plug.Conn @@ -19,7 +18,7 @@ def init(options) do def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn - def call(conn, _) do + def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _) do if Config.get!([:instance, :privileged_staff]) do conn else @@ -28,4 +27,10 @@ def call(conn, _) do |> halt() end end + + def call(conn, _) do + conn + |> render_error(:forbidden, "User is not a staff member.") + |> halt() + end end From fa35e24a5ec70ecd92e9e31d1e13da44b9e27b6d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:05:35 -0600 Subject: [PATCH 72/82] Apps: add user_id index --- priv/repo/migrations/20210818023112_add_user_id_to_apps.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs index 39e7fbef5..88a6bce00 100644 --- a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs +++ b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs @@ -5,5 +5,7 @@ def change do alter table(:apps) do add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) end + + create_if_not_exists(index(:apps, [:user_id])) end end From 2e4a1c56c36fcd4b9ef34bd3a771abfe21cc71d5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:14:15 -0600 Subject: [PATCH 73/82] AppController: test creating with and without a user --- .../controllers/app_controller.ex | 13 ++++----- .../controllers/app_controller_test.exs | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 079382b17..ef7331bf3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -28,15 +28,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation @doc "POST /api/v1/apps" - def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do + def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) - - user_id = - with %User{id: id} <- user do - id - else - _ -> nil - end + user_id = get_user_id(conn) app_attrs = params @@ -50,6 +44,9 @@ def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do end end + defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id + defp get_user_id(_conn), do: nil + @doc """ GET /api/v1/apps/verify_credentials Gets compact non-secret representation of the app. Supports app tokens and user tokens. diff --git a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs index 76d81b942..bfbb7f32d 100644 --- a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs @@ -35,6 +35,33 @@ test "apps/verify_credentials", %{conn: conn} do end test "creates an oauth app", %{conn: conn} do + app_attrs = build(:oauth_app) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = Repo.all(App) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(conn, 200) + assert app.user_id == nil + end + + test "creates an oauth app with a user", %{conn: conn} do user = insert(:user) app_attrs = build(:oauth_app) @@ -60,5 +87,6 @@ test "creates an oauth app", %{conn: conn} do } assert expected == json_response_and_validate_schema(conn, 200) + assert app.user_id == user.id end end From cb2a072e6252b7c3f6473f7cfd1af5c0ec732d7b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:29:03 -0600 Subject: [PATCH 74/82] Apps: add test for get_user_apps/1 --- test/pleroma/web/o_auth/app_test.exs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs index fc2f0d940..a5223b0a5 100644 --- a/test/pleroma/web/o_auth/app_test.exs +++ b/test/pleroma/web/o_auth/app_test.exs @@ -41,4 +41,16 @@ test "has unique client_id" do assert error.type == :unique end end + + test "get_user_apps/1" do + user = insert(:user) + + apps = [ + insert(:oauth_app, user_id: user.id), + insert(:oauth_app, user_id: user.id), + insert(:oauth_app, user_id: user.id) + ] + + assert App.get_user_apps(user) == apps + end end From 7704a722c06c9658d4037167dc5b6f01a4582b14 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:30:16 -0600 Subject: [PATCH 75/82] AppController: remove unnecessary `require Logger` --- lib/pleroma/web/mastodon_api/controllers/app_controller.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index ef7331bf3..8d18140ad 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token - require Logger - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) From 5c80d4087df2f6a8436af87ad109eb9e3bd4e3c1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:52:34 -0600 Subject: [PATCH 76/82] PleromaAPI.AppView: add test --- .../web/pleroma_api/views/app_view_test.exs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/pleroma/web/pleroma_api/views/app_view_test.exs diff --git a/test/pleroma/web/pleroma_api/views/app_view_test.exs b/test/pleroma/web/pleroma_api/views/app_view_test.exs new file mode 100644 index 000000000..f0aee6987 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/app_view_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppViewTest do + use Pleroma.DataCase, async: true + alias Pleroma.Web.PleromaAPI.AppView + import Pleroma.Factory + + test "index.json" do + apps = [ + insert(:oauth_app), + insert(:oauth_app), + insert(:oauth_app) + ] + + results = AppView.render("index.json", %{apps: apps}) + + assert [%{client_id: _, client_secret: _}, _, _] = results + end +end From f734579965b6f1a635e0622356e9cf6d4fff00bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 28 Dec 2021 16:11:17 +0100 Subject: [PATCH 77/82] MastoAPI: Add `GET /api/v1/accounts/lookup` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../api_spec/operations/account_operation.ex | 20 ++++++++++++++++ .../controllers/account_controller.ex | 12 ++++++++++ lib/pleroma/web/router.ex | 2 ++ .../controllers/account_controller_test.exs | 24 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..5836cab50 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -371,6 +371,26 @@ def blocks_operation do } end + def lookup_operation do + %Operation{ + tags: ["Account lookup"], + summary: "Find a user by nickname", + operationId: "AccountController.lookup", + parameters: [ + Operation.parameter( + :acct, + :query, + :string, + "User nickname" + ) + ], + responses: %{ + 200 => Operation.response("Account", "application/json", Account), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + def endorsements_operation do %Operation{ tags: ["Retrieve account information"], diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 5fcbffc34..3eae0a646 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -477,6 +477,18 @@ def blocks(%{assigns: %{user: user}} = conn, params) do |> render("index.json", users: users, for: user, as: :user) end + @doc "GET /api/v1/accounts/lookup" + def lookup(%{assigns: %{user: for_user}} = conn, %{acct: nickname} = _params) do + with %User{} = user <- User.get_by_nickname(nickname) do + render(conn, "show.json", + user: user, + for: for_user + ) + else + error -> user_visibility_error(conn, error) + end + end + @doc "GET /api/v1/endorsements" def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 5fbc2509e..ae373e58c 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -573,6 +573,8 @@ defmodule Pleroma.Web.Router do get("/accounts/search", SearchController, :account_search) get("/search", SearchController, :search) + get("/accounts/lookup", AccountController, :lookup) + get("/accounts/:id/statuses", AccountController, :statuses) get("/accounts/:id/followers", AccountController, :followers) get("/accounts/:id/following", AccountController, :following) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index a92a58224..86349619e 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1776,4 +1776,28 @@ test "getting a list of blocks" do assert [%{"id" => ^id2}] = result end + + test "account lookup", %{conn: conn} do + %{nickname: acct} = insert(:user, %{nickname: "nickname"}) + %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"}) + + result = + conn + |> get("/api/v1/accounts/lookup?acct=#{acct}") + |> json_response_and_validate_schema(200) + + assert %{"acct" => ^acct} = result + + result = + conn + |> get("/api/v1/accounts/lookup?acct=#{acct_two}") + |> json_response_and_validate_schema(200) + + assert %{"acct" => ^acct_two} = result + + _result = + conn + |> get("/api/v1/accounts/lookup?acct=unexisting_nickname") + |> json_response_and_validate_schema(404) + end end From 0dd1caa841386b99bcbe4adeef2c1cde5e6a377a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 28 Dec 2021 18:24:48 +0100 Subject: [PATCH 78/82] AccountController.lookup: skip visibility check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../web/mastodon_api/controllers/account_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 399a34217..6d8fcd026 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -493,11 +493,11 @@ def blocks(%{assigns: %{user: user}} = conn, params) do end @doc "GET /api/v1/accounts/lookup" - def lookup(%{assigns: %{user: for_user}} = conn, %{acct: nickname} = _params) do + def lookup(conn, %{acct: nickname} = _params) do with %User{} = user <- User.get_by_nickname(nickname) do render(conn, "show.json", user: user, - for: for_user + skip_visibility_check: true ) else error -> user_visibility_error(conn, error) From 1657db656cef7a6947e76d5213a04a1764a19cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 28 Dec 2021 20:02:59 +0100 Subject: [PATCH 79/82] AccountController.lookup: skip auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 6d8fcd026..a307807a9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(:skip_auth when action == :create) + plug(:skip_auth when action in [:create, :lookup]) plug(:skip_public_check when action in [:show, :statuses]) From 5f87472cdf60b987376d8fa6d2123f8acbed0bd9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 28 Dec 2021 15:11:38 -0600 Subject: [PATCH 80/82] Update CHANGELOG.md --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e527f32de..79b669782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. - AdminAPI: allow moderators to manage reports, users, invites, and custom emojis - AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc +- PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction` +- MastoAPI: Add `GET /api/v1/accounts/lookup` +- MastoAPI: Profile Directory support +- MastoAPI: Support v2 Suggestions (handpicked accounts only) +- Ability to log slow Ecto queries by configuring `:pleroma, :telemetry, :slow_queries_logging` +- Added Phoenix LiveDashboard at `/phoenix/live_dashboard` +- Added `/manifest.json` for progressive web apps. ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies - Handle Reject for already-accepted Follows properly - Display OpenGraph data on alternative notice routes. +- Fix replies count for remote replies +- ChatAPI: Add link headers +- Limited number of search results to 40 to prevent DoS attacks +- ActivityPub: fixed federation of attachment dimensions +- Fixed benchmarks +- Elixir 1.13 support +- Fixed crash when pinned_objects is nil +- Fixed slow timelines when there are a lot of deactivated users +- Fixed account deletion API ### Removed From 0c7fb520bf7d6d164a2334a23066d1188b2ec0e1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 29 Dec 2021 11:41:21 +0300 Subject: [PATCH 81/82] Added index on [:target_id, :relationship_type] to :user_relationships (speeds up `Notification.exclude_blockers/_`). --- ...r_relationships_target_id_relationship_type_index.exs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs diff --git a/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs b/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs new file mode 100644 index 000000000..fcefa6508 --- /dev/null +++ b/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.UserRelationshipsTargetIdRelationshipTypeIndex do + use Ecto.Migration + + def change do + create_if_not_exists( + index(:user_relationships, [:target_id, :relationship_type]) + ) + end +end From a7bdefc208044ef2ad6ba05f646f1cfa1df8e06b Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 29 Dec 2021 11:44:33 +0300 Subject: [PATCH 82/82] `mix format` --- ...1_user_relationships_target_id_relationship_type_index.exs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs b/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs index fcefa6508..f3eb8409f 100644 --- a/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs +++ b/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs @@ -2,8 +2,6 @@ defmodule Pleroma.Repo.Migrations.UserRelationshipsTargetIdRelationshipTypeIndex use Ecto.Migration def change do - create_if_not_exists( - index(:user_relationships, [:target_id, :relationship_type]) - ) + create_if_not_exists(index(:user_relationships, [:target_id, :relationship_type])) end end