From 6754d1f27239d3d529a3f667a6a93b267041daf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 16 Mar 2022 14:39:02 +0100 Subject: [PATCH 01/45] POST /api/v1/accounts/:id/remove_from_followers 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 | 16 +++++++++++ .../controllers/account_controller.ex | 19 ++++++++++--- lib/pleroma/web/router.ex | 1 + .../controllers/account_controller_test.exs | 27 +++++++++++++++++++ 4 files changed, 60 insertions(+), 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 026e92c5dc..2a60cab78b 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -370,6 +370,22 @@ def unendorse_operation do } end + def remove_from_followers_operation do + %Operation{ + tags: ["Account actions"], + summary: "Remove from followers", + operationId: "AccountController.remove_from_followers", + security: [%{"oAuth" => ["follow", "write:follows"]}], + description: "Remove the given account from followers", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship), + 400 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + def note_operation do %Operation{ tags: ["Account actions"], diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index f15305f9cc..31d75ba852 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -76,16 +76,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, - %{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow] + %{scopes: ["follow", "write:follows"]} + when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers] ) plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) - @relationship_actions [:follow, :unfollow] + @relationship_actions [:follow, :unfollow, :remove_from_followers] @needs_account ~W( - followers following lists follow unfollow mute unmute block unblock note endorse unendorse + followers following lists follow unfollow mute unmute block unblock + note endorse unendorse remove_from_followers )a plug( @@ -472,6 +474,17 @@ def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) end end + @doc "POST /api/v1/accounts/:id/remove_from_followers" + def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do + {:error, "Can not unfollow yourself"} + end + + def remove_from_followers(%{assigns: %{user: follower, account: followed}} = conn, _params) do + with {:ok, follower} <- CommonAPI.unfollow(followed, follower) do + render(conn, "relationship.json", user: follower, target: followed) + 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/router.ex b/lib/pleroma/web/router.ex index ceb6c3cfd7..8dc75b01ea 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -491,6 +491,7 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/note", AccountController, :note) post("/accounts/:id/pin", AccountController, :endorse) post("/accounts/:id/unpin", AccountController, :unendorse) + post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers) get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) 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 853d2c1113..b9ee173d66 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1976,4 +1976,31 @@ test "max pinned accounts", %{user: user, conn: conn} do |> json_response_and_validate_schema(400) end end + + describe "remove from followers" do + setup do: oauth_access(["follow"]) + + test "removing user from followers", %{conn: conn, user: user} do + %{id: other_user_id} = other_user = insert(:user) + + CommonAPI.follow(other_user, user) + + assert %{"id" => _id, "followed_by" => false} = + conn + |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers") + |> json_response_and_validate_schema(200) + end + + test "removing user from followers errors", %{user: user, conn: conn} do + # self remove + conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers") + + assert %{"error" => "Can not unfollow yourself"} = + json_response_and_validate_schema(conn_res, 400) + + # remove non existing user + conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers") + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404) + end + end end From ffe081bf4417ae7efbf24e4eaf0ee65fa2c2d8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 16 Mar 2022 18:38:28 +0100 Subject: [PATCH 02/45] Use reject_follow_request 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 | 7 +++++-- .../mastodon_api/controllers/account_controller_test.exs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 31d75ba852..50dd0e4c2e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -479,9 +479,12 @@ def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _p {:error, "Can not unfollow yourself"} end - def remove_from_followers(%{assigns: %{user: follower, account: followed}} = conn, _params) do - with {:ok, follower} <- CommonAPI.unfollow(followed, follower) do + def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do + with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do render(conn, "relationship.json", user: follower, target: followed) + else + nil -> + render_error(conn, :not_found, "Record not found") 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 b9ee173d66..f38ebdd75b 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1985,7 +1985,7 @@ test "removing user from followers", %{conn: conn, user: user} do CommonAPI.follow(other_user, user) - assert %{"id" => _id, "followed_by" => false} = + assert %{"id" => other_user_id, "followed_by" => false} = conn |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers") |> json_response_and_validate_schema(200) From 85cbf773f010b1bb2c77e51b1e994314bbf4f008 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Mar 2022 13:32:12 +0100 Subject: [PATCH 03/45] update sweet_xml [Security] --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index db2f1f069a..9b4a3e239c 100644 --- a/mix.exs +++ b/mix.exs @@ -141,7 +141,7 @@ defp deps do {:mogrify, "~> 0.7.4"}, {:ex_aws, "~> 2.1.6"}, {:ex_aws_s3, "~> 2.0"}, - {:sweet_xml, "~> 0.6.6"}, + {:sweet_xml, "~> 0.7.2"}, {:earmark, "1.4.15"}, {:bbcode_pleroma, "~> 0.2.0"}, {:crypt, diff --git a/mix.lock b/mix.lock index 232649cd5d..821c397b44 100644 --- a/mix.lock +++ b/mix.lock @@ -114,7 +114,7 @@ "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]}, "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, - "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, + "sweet_xml": {:hex, :sweet_xml, "0.7.2", "4729f997286811fabdd8288f8474e0840a76573051062f066c4b597e76f14f9f", [:mix], [], "hexpm", "6894e68a120f454534d99045ea3325f7740ea71260bc315f82e29731d570a6e8"}, "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"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, From 4d482b765f8bebbad0d5e9e17fb923eb475313d6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 18:39:34 -0400 Subject: [PATCH 04/45] Allow to skip cache in Cache plug Ref: fix-local-public --- lib/pleroma/web/plugs/cache.ex | 19 ++++++++++++------- test/pleroma/web/plugs/cache_test.exs | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index 1118548599..e0467f1079 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -98,14 +98,19 @@ defp cache_resp(conn, opts) do content_type = content_type(conn) conn = - unless opts[:tracking_fun] do - @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) - conn - else - tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) - @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + cond do + Map.get(conn.assigns, :skip_cache, false) -> + conn - opts.tracking_fun.(conn, tracking_fun_data) + !opts[:tracking_fun] -> + @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) + conn + + true -> + tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + + opts.tracking_fun.(conn, tracking_fun_data) end put_resp_header(conn, "x-cache", "MISS from Pleroma") diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs index 0ceab6cab0..4e729cafbb 100644 --- a/test/pleroma/web/plugs/cache_test.exs +++ b/test/pleroma/web/plugs/cache_test.exs @@ -179,4 +179,22 @@ test "ignore non-successful responses" do |> send_resp(:im_a_teapot, "🥤") |> sent_resp() end + + test "ignores if skip_cache is assigned" do + assert @miss_resp == + conn(:get, "/") + |> assign(:skip_cache, true) + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @miss_resp == + conn(:get, "/") + |> assign(:skip_cache, true) + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end end From fa3157df964d4f88d0fd1ce466a44333c8c7ef60 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 19:20:32 -0400 Subject: [PATCH 05/45] Skip cache when /objects or /activities is authenticated Ref: fix-local-public --- .../activity_pub/activity_pub_controller.ex | 11 +++++++++ lib/pleroma/web/plugs/cache.ex | 21 +++++++++------- .../activity_pub_controller_test.exs | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 57ac40b428..d423b1139b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -84,6 +84,7 @@ def object(%{assigns: assigns} = conn, _) do user <- Map.get(assigns, :user, nil), {_, true} <- {:visible?, Visibility.visible_for_user?(object, user)} do conn + |> maybe_skip_cache(user) |> assign(:tracking_fun_data, object.id) |> set_cache_ttl_for(object) |> put_resp_content_type("application/activity+json") @@ -112,6 +113,7 @@ def activity(%{assigns: assigns} = conn, _) do user <- Map.get(assigns, :user, nil), {_, true} <- {:visible?, Visibility.visible_for_user?(activity, user)} do conn + |> maybe_skip_cache(user) |> maybe_set_tracking_data(activity) |> set_cache_ttl_for(activity) |> put_resp_content_type("application/activity+json") @@ -151,6 +153,15 @@ defp set_cache_ttl_for(conn, entity) do assign(conn, :cache_ttl, ttl) end + def maybe_skip_cache(conn, user) do + if user do + conn + |> assign(:skip_cache, true) + else + conn + end + end + # GET /relay/following def relay_following(conn, _params) do with %{halted: false} = conn <- FederatingPlug.call(conn, []) do diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index e0467f1079..935b2d8346 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -97,20 +97,23 @@ defp cache_resp(conn, opts) do key = cache_key(conn, opts) content_type = content_type(conn) + should_cache = not Map.get(conn.assigns, :skip_cache, false) + conn = - cond do - Map.get(conn.assigns, :skip_cache, false) -> - conn - - !opts[:tracking_fun] -> + unless opts[:tracking_fun] do + if should_cache do @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) - conn + end - true -> - tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + conn + else + tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) + + if should_cache do @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + end - opts.tracking_fun.(conn, tracking_fun_data) + opts.tracking_fun.(conn, tracking_fun_data) end put_resp_header(conn, "x-cache", "MISS from Pleroma") diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 50315e21fc..5114056241 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -291,6 +291,30 @@ test "it returns a json representation of the object with accept application/ld+ assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) end + test "does not cache authenticated response", %{conn: conn} do + user = insert(:user) + reader = insert(:user) + + {:ok, post} = + CommonAPI.post(user, %{status: "test @#{reader.nickname}", visibility: "local"}) + + object = Object.normalize(post, fetch: false) + uuid = String.split(object.data["id"], "/") |> List.last() + + assert response = + conn + |> assign(:user, reader) + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + json_response(response, 200) + + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + |> json_response(404) + end + test "it returns 404 for non-public messages", %{conn: conn} do note = insert(:direct_note) uuid = String.split(note.data["id"], "/") |> List.last() From 57c486014c06715ff5cd5ad4361155d4a1776c23 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 6 May 2022 08:59:36 +0200 Subject: [PATCH 06/45] Release 2.4.3 --- CHANGELOG.md | 6 ++++++ mix.exs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ad0ada92..95405bb604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed +## 2.4.3 - 2022-05-06 + +### Security +- Private `/objects/` and `/activities/` leaking if cached by authenticated user +- SweetXML library DTD bomb + ## 2.4.2 - 2022-01-10 ### Fixed diff --git a/mix.exs b/mix.exs index 9b4a3e239c..927f39975f 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.4.2"), + version: version("2.4.3"), elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), From 9022d855cd08db104b3a52597e9c02a14b1bcb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 8 Jul 2022 13:42:01 +0200 Subject: [PATCH 07/45] Check refute User.following? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- test/pleroma/user_test.exs | 6 +++--- test/pleroma/web/activity_pub/activity_pub_test.exs | 2 +- .../mastodon_api/controllers/account_controller_test.exs | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index bb28a3f813..34ec400296 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -310,7 +310,7 @@ test "local users do not automatically follow local locked accounts" do describe "unfollow/2" do setup do: clear_config([:instance, :external_user_synchronization]) - test "unfollow with syncronizes external user" do + test "unfollow with synchronizes external user" do clear_config([:instance, :external_user_synchronization], true) followed = @@ -2236,7 +2236,7 @@ test "updates the counters normally on following/getting a follow when disabled" assert other_user.follower_count == 1 end - test "syncronizes the counters with the remote instance for the followed when enabled" do + test "synchronizes the counters with the remote instance for the followed when enabled" do clear_config([:instance, :external_user_synchronization], false) user = insert(:user) @@ -2258,7 +2258,7 @@ test "syncronizes the counters with the remote instance for the followed when en assert other_user.follower_count == 437 end - test "syncronizes the counters with the remote instance for the follower when enabled" do + test "synchronizes the counters with the remote instance for the follower when enabled" do clear_config([:instance, :external_user_synchronization], false) user = insert(:user) diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 8aa586f409..181397fa05 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1665,7 +1665,7 @@ test "fetches only public posts for other users" do end describe "fetch_follow_information_for_user" do - test "syncronizes following/followers counters" do + test "synchronizes following/followers counters" do user = insert(:user, local: false, 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 f38ebdd75b..8311ebff93 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1989,6 +1989,8 @@ test "removing user from followers", %{conn: conn, user: user} do conn |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers") |> json_response_and_validate_schema(200) + + refute User.following?(other_user, user) end test "removing user from followers errors", %{user: user, conn: conn} do From a7f01ffc1d0795f65b34b6dd9337d665f27edff9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 00:34:04 -0400 Subject: [PATCH 08/45] Make backups require its own scope --- lib/pleroma/web/pleroma_api/controllers/backup_controller.ex | 2 +- .../web/pleroma_api/controllers/backup_controller_test.exs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex index 1a0548295a..b9daed22bc 100644 --- a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create]) + plug(OAuthScopesPlug, %{scopes: ["read:backups"]} when action in [:index, :create]) plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation diff --git a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs index 3b4b1bfffe..a758925b7e 100644 --- a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs @@ -11,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do setup do clear_config([Pleroma.Upload, :uploader]) clear_config([Backup, :limit_days]) - oauth_access(["read:accounts"]) + oauth_access(["read:backups"]) end test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do @@ -85,7 +85,7 @@ test "POST /api/v1/pleroma/backups", %{user: _user, conn: conn} do test "Backup without email address" do user = Pleroma.Factory.insert(:user, email: nil) - %{conn: conn} = oauth_access(["read:accounts"], user: user) + %{conn: conn} = oauth_access(["read:backups"], user: user) assert is_nil(user.email) From 738ca484fd812d3fc027d4c3037d307c61fa24ca Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 18:15:25 -0400 Subject: [PATCH 09/45] Update api spec to reflect OAuth scope change --- .../web/api_spec/operations/pleroma_backup_operation.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex index 82ec1e7bb7..45fa2b0582 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex @@ -16,7 +16,7 @@ def index_operation do %Operation{ tags: ["Backups"], summary: "List backups", - security: [%{"oAuth" => ["read:account"]}], + security: [%{"oAuth" => ["read:backups"]}], operationId: "PleromaAPI.BackupController.index", responses: %{ 200 => @@ -37,7 +37,7 @@ def create_operation do %Operation{ tags: ["Backups"], summary: "Create a backup", - security: [%{"oAuth" => ["read:account"]}], + security: [%{"oAuth" => ["read:backups"]}], operationId: "PleromaAPI.BackupController.create", responses: %{ 200 => From e06f2b9f5ea58c90cafd7864a66809fe8ea0a96f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 18:17:07 -0400 Subject: [PATCH 10/45] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ed9bbadf..9e4cb9a4ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Allow users to remove their emails if instance does not need email to register - Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation` +- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts` ### Added - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object From c62a4f1c173490ad64fdfbab0c005ca3523b6013 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 13:19:38 -0400 Subject: [PATCH 11/45] Disconnect streaming sessions when token is revoked --- .../web/mastodon_api/websocket_handler.ex | 8 ++- .../web/o_auth/token/strategy/revoke.ex | 1 + lib/pleroma/web/streamer.ex | 24 +++++++-- test/pleroma/web/streamer_test.exs | 54 +++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 0d1faffbd5..ffbc2c4de5 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -32,7 +32,7 @@ def init(%{qs: qs} = req, state) do req end - {:cowboy_websocket, req, %{user: user, topic: topic, count: 0, timer: nil}, + {:cowboy_websocket, req, %{user: user, topic: topic, oauth_token: oauth_token, count: 0, timer: nil}, %{idle_timeout: @timeout}} else {:error, :bad_topic} -> @@ -54,7 +54,7 @@ def websocket_init(state) do }, topic #{state.topic}" ) - Streamer.add_socket(state.topic, state.user) + Streamer.add_socket(state.topic, state.oauth_token) {:ok, %{state | timer: timer()}} end @@ -100,6 +100,10 @@ def websocket_info(:tick, state) do {:reply, :ping, %{state | timer: nil, count: 0}, :hibernate} end + def websocket_info(:close, state) do + {:stop, state} + end + # State can be `[]` only in case we terminate before switching to websocket, # we already log errors for these cases in `init/1`, so just do nothing here def terminate(_reason, _req, []), do: :ok diff --git a/lib/pleroma/web/o_auth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex index 8d65727041..03a0b91aee 100644 --- a/lib/pleroma/web/o_auth/token/strategy/revoke.ex +++ b/lib/pleroma/web/o_auth/token/strategy/revoke.ex @@ -22,5 +22,6 @@ def revoke(%App{} = app, %{"token" => token} = _attrs) do @spec revoke(Token.t()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()} def revoke(%Token{} = token) do Repo.delete(token) + Pleroma.Web.Streamer.close_streams_by_oauth_token(token) end end diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index fc3bbb1302..8bf70d99be 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -37,7 +37,7 @@ def registry, do: @registry {:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized} def get_topic_and_add_socket(stream, user, oauth_token, params \\ %{}) do with {:ok, topic} <- get_topic(stream, user, oauth_token, params) do - add_socket(topic, user) + add_socket(topic, oauth_token) end end @@ -120,10 +120,10 @@ def get_topic(_stream, _user, _oauth_token, _params) do end @doc "Registers the process for streaming. Use `get_topic/3` to get the full authorized topic." - def add_socket(topic, user) do + def add_socket(topic, oauth_token) do if should_env_send?() do - auth? = if user, do: true - Registry.register(@registry, topic, auth?) + oauth_token_id = if oauth_token, do: oauth_token.id, else: false + Registry.register(@registry, topic, oauth_token_id) end {:ok, topic} @@ -320,6 +320,22 @@ defp thread_containment(activity, user) do end end + def close_streams_by_oauth_token(oauth_token) do + if should_env_send?() do + Registry.select( + @registry, + [ + { + {:"$1", :"$2", :"$3"}, + [{:==, :"$3", oauth_token.id}], + [:"$2"] + } + ] + ) + |> Enum.each(fn pid -> send(pid, :close) end) + end + end + # In test environement, only return true if the registry is started. # In benchmark environment, returns false. # In any other environment, always returns true. diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs index b788a91386..5426467e5e 100644 --- a/test/pleroma/web/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -813,4 +813,58 @@ test "it sends conversation update to the 'direct' stream when a message is dele assert last_status["id"] == to_string(create_activity.id) end end + + describe "stop streaming if token got revoked" do + test "do not revoke other tokens" do + %{user: user, token: token} = oauth_access(["read"]) + %{token: token2} = oauth_access(["read"], user: user) + %{user: user2, token: user2_token} = oauth_access(["read"]) + + post_user = insert(:user) + CommonAPI.follow(user, post_user) + CommonAPI.follow(user2, post_user) + + Streamer.get_topic_and_add_socket("user", user, token) + Streamer.get_topic_and_add_socket("user", user, token2) + Streamer.get_topic_and_add_socket("user", user2, user2_token) + + {:ok, _} = + CommonAPI.post(post_user, %{ + status: "hi" + }) + + assert_receive {:render_with_user, _, "update.json", _} + assert_receive {:render_with_user, _, "update.json", _} + assert_receive {:render_with_user, _, "update.json", _} + + Pleroma.Web.OAuth.Token.Strategy.Revoke.revoke(token) + + assert_receive :close + refute_receive :close + end + + test "revoke all streams for this token" do + %{user: user, token: token} = oauth_access(["read"]) + + post_user = insert(:user) + CommonAPI.follow(user, post_user) + + Streamer.get_topic_and_add_socket("user", user, token) + Streamer.get_topic_and_add_socket("user", user, token) + + {:ok, _} = + CommonAPI.post(post_user, %{ + status: "hi" + }) + + assert_receive {:render_with_user, _, "update.json", _} + assert_receive {:render_with_user, _, "update.json", _} + + Pleroma.Web.OAuth.Token.Strategy.Revoke.revoke(token) + + assert_receive :close + assert_receive :close + refute_receive :close + end + end end From eb42e90c4f9ca35a6dc0e84e6f87b6f4b680173c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 13:56:39 -0400 Subject: [PATCH 12/45] Use Websockex to replace websocket_client --- mix.exs | 2 +- mix.lock | 2 +- .../integration/mastodon_websocket_test.exs | 14 +++++----- test/support/websocket_client.ex | 28 +++++++++---------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/mix.exs b/mix.exs index 927f39975f..46c9fcaa2e 100644 --- a/mix.exs +++ b/mix.exs @@ -210,7 +210,7 @@ defp deps do {:excoveralls, "0.12.3", only: :test}, {:hackney, "~> 1.18.0", override: true}, {:mox, "~> 1.0", only: :test}, - {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} + {:websockex, "~> 0.4.3", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index 821c397b44..1fe713e8e2 100644 --- a/mix.lock +++ b/mix.lock @@ -126,5 +126,5 @@ "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "web_push_encryption": {:git, "https://github.com/lanodan/elixir-web-push-encryption.git", "026a043037a89db4da8f07560bc8f9c68bcf0cc0", [branch: "bugfix/otp-24"]}, - "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []}, + "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, } diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 43ec57893c..1e0319144c 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -33,16 +33,16 @@ def start_socket(qs \\ nil, headers \\ []) do test "refuses invalid requests" do capture_log(fn -> - assert {:error, {404, _}} = start_socket() - assert {:error, {404, _}} = start_socket("?stream=ncjdk") + assert {:error, %WebSockex.RequestError{code: 404}} = start_socket() + assert {:error, %WebSockex.RequestError{code: 404}} = start_socket("?stream=ncjdk") Process.sleep(30) end) end test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -102,7 +102,7 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -111,7 +111,7 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user:notification") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user:notification") Process.sleep(30) end) end @@ -120,7 +120,7 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) capture_log(fn -> - assert {:error, {401, _}} = + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index 34b9554743..2660f61517 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -5,18 +5,17 @@ defmodule Pleroma.Integration.WebsocketClient do # https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs + use WebSockex + @doc """ Starts the WebSocket server for given ws URL. Received Socket.Message's are forwarded to the sender pid """ def start_link(sender, url, headers \\ []) do - :crypto.start() - :ssl.start() - - :websocket_client.start_link( - String.to_charlist(url), + WebSockex.start_link( + url, __MODULE__, - [sender], + %{ sender: sender }, extra_headers: headers ) end @@ -36,27 +35,26 @@ def send_text(server_pid, msg) do end @doc false - def init([sender], _conn_state) do - {:ok, %{sender: sender}} - end - - @doc false - def websocket_handle(frame, _conn_state, state) do + @impl true + def handle_frame(frame, state) do send(state.sender, frame) {:ok, state} end @doc false - def websocket_info({:text, msg}, _conn_state, state) do + @impl true + def handle_info({:text, msg}, state) do {:reply, {:text, msg}, state} end - def websocket_info(:close, _conn_state, _state) do + @impl true + def handle_info(:close, _state) do {:close, <<>>, "done"} end @doc false - def websocket_terminate(_reason, _conn_state, _state) do + @impl true + def terminate(_reason, _state) do :ok end end From 3522852c6196cafa63804240f52dd593e09ba694 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 14:09:42 -0400 Subject: [PATCH 13/45] Test that server will disconnect websocket upon token revocation --- .../integration/mastodon_websocket_test.exs | 18 +++++++++++++++++- test/support/websocket_client.ex | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 1e0319144c..adb2d70047 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -91,7 +91,7 @@ test "receives well formatted events" do {:ok, token} = OAuth.Token.exchange_token(app, auth) - %{user: user, token: token} + %{app: app, user: user, token: token} end test "accepts valid tokens", state do @@ -126,5 +126,21 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do Process.sleep(30) end) end + + test "disconnect when token is revoked", %{app: app, user: user, token: token} do + assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") + assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") + + {:ok, auth} = OAuth.Authorization.create_authorization(app, user) + + {:ok, token2} = OAuth.Token.exchange_token(app, auth) + assert {:ok, _} = start_socket("?stream=user&access_token=#{token2.token}") + + OAuth.Token.Strategy.Revoke.revoke(token) + + assert_receive {:close, _} + assert_receive {:close, _} + refute_receive {:close, _} + end end end diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index 2660f61517..abe7d5eda4 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -41,6 +41,12 @@ def handle_frame(frame, state) do {:ok, state} end + @impl true + def handle_disconnect(conn_status, state) do + send(state.sender, {:close, conn_status}) + {:ok, state} + end + @doc false @impl true def handle_info({:text, msg}, state) do From f459c1260b43396fb7173e97e29ccef441a615ec Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 14:10:07 -0400 Subject: [PATCH 14/45] Lint --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 3 ++- test/pleroma/integration/mastodon_websocket_test.exs | 8 ++++++-- test/support/websocket_client.ex | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index ffbc2c4de5..930e9eb293 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -32,7 +32,8 @@ def init(%{qs: qs} = req, state) do req end - {:cowboy_websocket, req, %{user: user, topic: topic, oauth_token: oauth_token, count: 0, timer: nil}, + {:cowboy_websocket, req, + %{user: user, topic: topic, oauth_token: oauth_token, count: 0, timer: nil}, %{idle_timeout: @timeout}} else {:error, :bad_topic} -> diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index adb2d70047..d44033842c 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -41,7 +41,9 @@ test "refuses invalid requests" do test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user") Process.sleep(30) end) @@ -111,7 +113,9 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user:notification") + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user:notification") + Process.sleep(30) end) end diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index abe7d5eda4..70d331999f 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -15,7 +15,7 @@ def start_link(sender, url, headers \\ []) do WebSockex.start_link( url, __MODULE__, - %{ sender: sender }, + %{sender: sender}, extra_headers: headers ) end From a31d6bb52c8856c71f20d49aec8948573dacba68 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 14:58:57 -0400 Subject: [PATCH 15/45] Execute session disconnect in background --- lib/pleroma/application.ex | 3 ++- lib/pleroma/web/o_auth/token/strategy/revoke.ex | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 9824e0a4ac..92d1436653 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -89,7 +89,8 @@ def start(_type, _args) do Pleroma.Repo, Config.TransferTask, Pleroma.Emoji, - Pleroma.Web.Plugs.RateLimiter.Supervisor + Pleroma.Web.Plugs.RateLimiter.Supervisor, + {Task.Supervisor, name: Pleroma.TaskSupervisor} ] ++ cachex_children() ++ http_children(adapter, @mix_env) ++ diff --git a/lib/pleroma/web/o_auth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex index 03a0b91aee..de99bc137e 100644 --- a/lib/pleroma/web/o_auth/token/strategy/revoke.ex +++ b/lib/pleroma/web/o_auth/token/strategy/revoke.ex @@ -21,7 +21,18 @@ def revoke(%App{} = app, %{"token" => token} = _attrs) do @doc "Revokes access token" @spec revoke(Token.t()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()} def revoke(%Token{} = token) do - Repo.delete(token) - Pleroma.Web.Streamer.close_streams_by_oauth_token(token) + with {:ok, token} <- Repo.delete(token) do + Task.Supervisor.start_child( + Pleroma.TaskSupervisor, + Pleroma.Web.Streamer, + :close_streams_by_oauth_token, + [token], + restart: :transient + ) + + {:ok, token} + else + result -> result + end end end From 5a2c8ef4ccfbcc996fb812779730c78e2a3fbdcd Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 19:58:16 -0400 Subject: [PATCH 16/45] Refactor streamer test --- test/pleroma/web/streamer_test.exs | 81 +++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs index 5426467e5e..7c4b9e2887 100644 --- a/test/pleroma/web/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -815,7 +815,47 @@ test "it sends conversation update to the 'direct' stream when a message is dele end describe "stop streaming if token got revoked" do - test "do not revoke other tokens" do + setup do + child_proc = fn start, finalize -> + fn -> + start.() + + receive do + {StreamerTest, :ready} -> + assert_receive {:render_with_user, _, "update.json", _} + + receive do + {StreamerTest, :revoked} -> finalize.() + end + end + end + end + + starter = fn user, token -> + fn -> Streamer.get_topic_and_add_socket("user", user, token) end + end + + hit = fn -> assert_receive :close end + miss = fn -> refute_receive :close end + + send_all = fn tasks, thing -> Enum.each(tasks, &send(&1.pid, thing)) end + + %{ + child_proc: child_proc, + starter: starter, + hit: hit, + miss: miss, + send_all: send_all + } + end + + test "do not revoke other tokens", %{ + child_proc: child_proc, + starter: starter, + hit: hit, + miss: miss, + send_all: send_all + } do %{user: user, token: token} = oauth_access(["read"]) %{token: token2} = oauth_access(["read"], user: user) %{user: user2, token: user2_token} = oauth_access(["read"]) @@ -824,47 +864,54 @@ test "do not revoke other tokens" do CommonAPI.follow(user, post_user) CommonAPI.follow(user2, post_user) - Streamer.get_topic_and_add_socket("user", user, token) - Streamer.get_topic_and_add_socket("user", user, token2) - Streamer.get_topic_and_add_socket("user", user2, user2_token) + tasks = [ + Task.async(child_proc.(starter.(user, token), hit)), + Task.async(child_proc.(starter.(user, token2), miss)), + Task.async(child_proc.(starter.(user2, user2_token), miss)) + ] {:ok, _} = CommonAPI.post(post_user, %{ status: "hi" }) - assert_receive {:render_with_user, _, "update.json", _} - assert_receive {:render_with_user, _, "update.json", _} - assert_receive {:render_with_user, _, "update.json", _} + send_all.(tasks, {StreamerTest, :ready}) Pleroma.Web.OAuth.Token.Strategy.Revoke.revoke(token) - assert_receive :close - refute_receive :close + send_all.(tasks, {StreamerTest, :revoked}) + + Enum.each(tasks, &Task.await/1) end - test "revoke all streams for this token" do + test "revoke all streams for this token", %{ + child_proc: child_proc, + starter: starter, + hit: hit, + send_all: send_all + } do %{user: user, token: token} = oauth_access(["read"]) post_user = insert(:user) CommonAPI.follow(user, post_user) - Streamer.get_topic_and_add_socket("user", user, token) - Streamer.get_topic_and_add_socket("user", user, token) + tasks = [ + Task.async(child_proc.(starter.(user, token), hit)), + Task.async(child_proc.(starter.(user, token), hit)) + ] {:ok, _} = CommonAPI.post(post_user, %{ status: "hi" }) - assert_receive {:render_with_user, _, "update.json", _} - assert_receive {:render_with_user, _, "update.json", _} + send_all.(tasks, {StreamerTest, :ready}) Pleroma.Web.OAuth.Token.Strategy.Revoke.revoke(token) - assert_receive :close - assert_receive :close - refute_receive :close + send_all.(tasks, {StreamerTest, :revoked}) + + Enum.each(tasks, &Task.await/1) end end end From 31fd41de0cbca28cd2461e96384460596e54e9e9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 20:29:06 -0400 Subject: [PATCH 17/45] Release 2.4.4 --- CHANGELOG.md | 5 +++++ mix.exs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95405bb604..bcbe3ba565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed +## 2.4.4 - 2022-08-19 + +### Security +- Streaming API sessions will now properly disconnect if the corresponding token is revoked + ## 2.4.3 - 2022-05-06 ### Security diff --git a/mix.exs b/mix.exs index 46c9fcaa2e..0e2834fc60 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.4.3"), + version: version("2.4.4"), elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), From 21ab7369cad6504be2f815aec888b38023d7a17a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 22:35:08 +0200 Subject: [PATCH 18/45] Bump minimum Elixir version to 1.10 With the release of Elixir 1.14, Elixir 1.9 is now end-of-life. Elixir 1.10 Release Notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 --- .gitlab-ci.yml | 4 ++-- CHANGELOG.md | 1 + Dockerfile | 4 ++-- ci/Dockerfile | 2 +- config/config.exs | 2 +- docs/configuration/howto_database_config.md | 2 +- docs/installation/generic_dependencies.include | 2 +- elixir_buildpack.config | 2 +- lib/mix/tasks/pleroma/config.ex | 9 ++------- lib/pleroma/config/loader.ex | 15 ++------------- lib/pleroma/web/activity_pub/object_validator.ex | 3 +-- mix.exs | 2 +- priv/templates/sample_config.eex | 6 +----- restarter/mix.exs | 2 +- 14 files changed, 18 insertions(+), 38 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0e7f4926a8..37ec483530 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -271,7 +271,7 @@ amd64: MIX_ENV: prod before_script: &before-release - apt-get update && apt-get install -y cmake libmagic-dev - - echo "import Mix.Config" > config/prod.secret.exs + - echo "import Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force script: &release @@ -290,7 +290,7 @@ amd64-musl: variables: *release-variables before_script: &before-release-musl - apk add git build-base cmake file-dev openssl - - echo "import Mix.Config" > config/prod.secret.exs + - echo "import Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force script: *release diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0ef4e113..caa5d0cd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - MastoFE ### Changed +- **Breaking:** Elixir >=1.10 is now required (was >= 1.9) - Allow users to remove their emails if instance does not need email to register - Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation` - Updated the recommended pleroma.vcl configuration for Varnish to target Varnish 7.0+ diff --git a/Dockerfile b/Dockerfile index e68b7ea7c3..334d954f74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM elixir:1.9-alpine as build +FROM elixir:1.10-alpine as build COPY . . ENV MIX_ENV=prod RUN apk add git gcc g++ musl-dev make cmake file-dev &&\ - echo "import Mix.Config" > config/prod.secret.exs &&\ + echo "import Config" > config/prod.secret.exs &&\ mix local.hex --force &&\ mix local.rebar --force &&\ mix deps.get --only prod &&\ diff --git a/ci/Dockerfile b/ci/Dockerfile index e6a8b438c1..5929f832d0 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.9.4 +FROM elixir:1.10.4 RUN apt-get update &&\ apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ diff --git a/config/config.exs b/config/config.exs index 1653358a0c..6adb63e5f4 100644 --- a/config/config.exs +++ b/config/config.exs @@ -37,7 +37,7 @@ # FIGURATION! EDIT YOUR SECRET FILE (either prod.secret.exs, dev.secret.exs). # # This file is responsible for configuring your application -# and its dependencies with the aid of the Mix.Config module. +# and its dependencies with the aid of the Config module. # # This configuration file is loaded before any dependency and # is restricted to this project. diff --git a/docs/configuration/howto_database_config.md b/docs/configuration/howto_database_config.md index ae1462f9b0..e5af9097a9 100644 --- a/docs/configuration/howto_database_config.md +++ b/docs/configuration/howto_database_config.md @@ -59,7 +59,7 @@ The configuration of Pleroma has traditionally been managed with a config file, Here is an example of a server config stripped down after migration: ``` - use Mix.Config + import Config config :pleroma, Pleroma.Web.Endpoint, url: [host: "cool.pleroma.site", scheme: "https", port: 443] diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include index 2dbd93e427..dcaacfdfda 100644 --- a/docs/installation/generic_dependencies.include +++ b/docs/installation/generic_dependencies.include @@ -1,7 +1,7 @@ ## Required dependencies * PostgreSQL 9.6+ -* Elixir 1.9+ +* Elixir 1.10+ * Erlang OTP 22.2+ * git * file / libmagic diff --git a/elixir_buildpack.config b/elixir_buildpack.config index 946408c12f..1102e71455 100644 --- a/elixir_buildpack.config +++ b/elixir_buildpack.config @@ -1,2 +1,2 @@ -elixir_version=1.9.4 +elixir_version=1.10.4 erlang_version=22.3.4.1 diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 33d147d362..3a2ea44f80 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -304,13 +304,8 @@ defp write_config(file, path, opts) do System.cmd("mix", ["format", path]) end - if Code.ensure_loaded?(Config.Reader) do - defp config_header, do: "import Config\r\n\r\n" - defp read_file(config_file), do: Config.Reader.read_imports!(config_file) - else - defp config_header, do: "use Mix.Config\r\n\r\n" - defp read_file(config_file), do: Mix.Config.eval!(config_file) - end + defp config_header, do: "import Config\r\n\r\n" + defp read_file(config_file), do: Config.Reader.read_imports!(config_file) defp write_and_delete(config, file, delete?) do config diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 015be3d8ed..bd85eccab5 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -19,21 +19,10 @@ defmodule Pleroma.Config.Loader do :tesla ] - if Code.ensure_loaded?(Config.Reader) do - @reader Config.Reader - - def read(path), do: @reader.read!(path) - else - # support for Elixir less than 1.9 - @reader Mix.Config - def read(path) do - path - |> @reader.eval!() - |> elem(0) - end - end + @reader Config.Reader @spec read(Path.t()) :: keyword() + def read(path), do: @reader.read!(path) @spec merge(keyword(), keyword()) :: keyword() def merge(c1, c2), do: @reader.merge(c1, c2) diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index f3e31c9319..21442687c3 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -204,8 +204,7 @@ def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page def cast_and_apply(o), do: {:error, {:validator_not_set, o}} - # is_struct/1 appears in Elixir 1.11 - def stringify_keys(%{__struct__: _} = object) do + def stringify_keys(object) when is_struct(object) do object |> Map.from_struct() |> stringify_keys diff --git a/mix.exs b/mix.exs index 81c4cf9aeb..a075cf05a3 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ def project do [ app: :pleroma, version: version("2.4.52"), - elixir: "~> 1.9", + elixir: "~> 1.10", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), elixirc_options: [warnings_as_errors: warnings_as_errors()], diff --git a/priv/templates/sample_config.eex b/priv/templates/sample_config.eex index 0068969ac4..d44c324caa 100644 --- a/priv/templates/sample_config.eex +++ b/priv/templates/sample_config.eex @@ -3,11 +3,7 @@ # NOTE: This file should not be committed to a repo or otherwise made public # without removing sensitive information. -<%= if Code.ensure_loaded?(Config) or not Code.ensure_loaded?(Mix.Config) do - "import Config" -else - "use Mix.Config" -end %> +import Config config :pleroma, Pleroma.Web.Endpoint, url: [host: "<%= domain %>", scheme: "https", port: <%= port %>], diff --git a/restarter/mix.exs b/restarter/mix.exs index 9f26f5f649..4bb9b76e21 100644 --- a/restarter/mix.exs +++ b/restarter/mix.exs @@ -5,7 +5,7 @@ def project do [ app: :restarter, version: "0.1.0", - elixir: "~> 1.8", + elixir: "~> 1.10", start_permanent: Mix.env() == :prod, deps: deps() ] From 6d148b6637af5eb96435cd802886d71b461b760e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 13:56:39 -0400 Subject: [PATCH 19/45] Use Websockex to replace websocket_client --- mix.exs | 2 +- mix.lock | 2 +- .../integration/mastodon_websocket_test.exs | 18 +++++++----- test/support/websocket_client.ex | 28 +++++++++---------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/mix.exs b/mix.exs index 6e84fe4825..4507831cfc 100644 --- a/mix.exs +++ b/mix.exs @@ -211,7 +211,7 @@ defp deps do {:excoveralls, "0.12.3", only: :test}, {:hackney, "~> 1.18.0", override: true}, {:mox, "~> 1.0", only: :test}, - {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} + {:websockex, "~> 0.4.3", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index 14e43c7034..405bc55651 100644 --- a/mix.lock +++ b/mix.lock @@ -134,5 +134,5 @@ "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"}, - "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []}, + "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, } diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index 2d4c7f63b2..0226b2a5de 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -33,16 +33,18 @@ def start_socket(qs \\ nil, headers \\ []) do test "refuses invalid requests" do capture_log(fn -> - assert {:error, {404, _}} = start_socket() - assert {:error, {404, _}} = start_socket("?stream=ncjdk") + assert {:error, %WebSockex.RequestError{code: 404}} = start_socket() + assert {:error, %WebSockex.RequestError{code: 404}} = start_socket("?stream=ncjdk") Process.sleep(30) end) end test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user&access_token=aaaaaaaaaaaa") + + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -102,7 +104,7 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user") + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -111,7 +113,9 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user:notification") + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user:notification") + Process.sleep(30) end) end @@ -120,7 +124,7 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) capture_log(fn -> - assert {:error, {401, _}} = + assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index d149b324ec..cf2972c38b 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -5,18 +5,17 @@ defmodule Pleroma.Integration.WebsocketClient do # https://github.com/phoenixframework/phoenix/blob/master/test/support/websocket_client.exs + use WebSockex + @doc """ Starts the WebSocket server for given ws URL. Received Socket.Message's are forwarded to the sender pid """ def start_link(sender, url, headers \\ []) do - :crypto.start() - :ssl.start() - - :websocket_client.start_link( - String.to_charlist(url), + WebSockex.start_link( + url, __MODULE__, - [sender], + %{sender: sender}, extra_headers: headers ) end @@ -36,27 +35,26 @@ def send_text(server_pid, msg) do end @doc false - def init([sender], _conn_state) do - {:ok, %{sender: sender}} - end - - @doc false - def websocket_handle(frame, _conn_state, state) do + @impl true + def handle_frame(frame, state) do send(state.sender, frame) {:ok, state} end @doc false - def websocket_info({:text, msg}, _conn_state, state) do + @impl true + def handle_info({:text, msg}, state) do {:reply, {:text, msg}, state} end - def websocket_info(:close, _conn_state, _state) do + @impl true + def handle_info(:close, _state) do {:close, <<>>, "done"} end @doc false - def websocket_terminate(_reason, _conn_state, _state) do + @impl true + def terminate(_reason, _state) do :ok end end From 2f301bbb87ca393f2b2355f53ee9de13fc020a5e Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 20:41:53 +0200 Subject: [PATCH 20/45] timeline_controller_test: Fix test name for elixir 1.14 --- .../web/mastodon_api/controllers/timeline_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index 1328b42c95..b13a8033ba 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -944,7 +944,7 @@ test "muted emotions", %{conn: conn} do end end - describe "hashtag timeline handling of :restrict_unauthenticated setting" do + describe "hashtag timeline handling of restrict_unauthenticated setting" do setup do user = insert(:user) {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"}) From 93ed6da4a393dc1e84c8b7ddbe81b25eb5baa205 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 21:04:09 +0200 Subject: [PATCH 21/45] mix: Switch prometheus_ex to fix/elixir-1.14 branch --- mix.exs | 4 ++-- mix.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 4507831cfc..66390d1137 100644 --- a/mix.exs +++ b/mix.exs @@ -166,8 +166,8 @@ defp deps do {:poolboy, "~> 1.5"}, {:prometheus, "~> 4.6"}, {:prometheus_ex, - git: "https://git.pleroma.social/pleroma/elixir-libraries/prometheus.ex.git", - ref: "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5", + git: "https://github.com/lanodan/prometheus.ex.git", + branch: "fix/elixir-1.14", override: true}, {:prometheus_plugs, "~> 1.1"}, {:prometheus_phoenix, "~> 1.3"}, diff --git a/mix.lock b/mix.lock index 405bc55651..4cb6fc7da9 100644 --- a/mix.lock +++ b/mix.lock @@ -110,7 +110,7 @@ "pot": {:hex, :pot, "1.0.1", "81b511b1fa7c3123171c265cb7065a1528cebd7277b0cbc94257c50a8b2e4c17", [:rebar3], [], "hexpm", "ed87f5976531d91528452faa1138a5328db7f9f20d8feaae15f5051f79bcfb6d"}, "prometheus": {:hex, :prometheus, "4.8.0", "1ce1e1002b173c336d61f186b56263346536e76814edd9a142e12aeb2d6c1ad2", [:mix, :rebar3], [], "hexpm", "0fc2e17103073edb3758a46a5d44b006191bf25b73cbaa2b779109de396afcb5"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, - "prometheus_ex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus.ex.git", "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5", [ref: "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5"]}, + "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex.git", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, "prometheus_phoenix": {:hex, :prometheus_phoenix, "1.3.0", "c4b527e0b3a9ef1af26bdcfbfad3998f37795b9185d475ca610fe4388fdd3bb5", [:mix], [{:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "c4d1404ac4e9d3d963da601db2a7d8ea31194f0017057fabf0cfb9bf5a6c8c75"}, "prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]}, "prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"}, From e124776d1448f9043d335dea9425578f37ad1a57 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 21:12:16 +0200 Subject: [PATCH 22/45] Elixir 1.14 formatting --- lib/mix/tasks/pleroma/user.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 50ffb7f27e..929fa17177 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -112,9 +112,10 @@ def run(["reset_password", nickname]) do {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do shell_info("Generated password reset token for #{user.nickname}") - IO.puts("URL: #{Pleroma.Web.Router.Helpers.reset_password_url(Pleroma.Web.Endpoint, - :reset, - token.token)}") + url = + Pleroma.Web.Router.Helpers.reset_password_url(Pleroma.Web.Endpoint, :reset, token.token) + + IO.puts("URL: #{url}") else _ -> shell_error("No local user #{nickname}") From 24af2e1c5811e5e85ede1f75f7845e09a477fb58 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 21:12:02 +0200 Subject: [PATCH 23/45] script_test: Fix %ErlangError for Elixir 1.14 --- .../web/media_proxy/invalidation/script_test.exs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/pleroma/web/media_proxy/invalidation/script_test.exs b/test/pleroma/web/media_proxy/invalidation/script_test.exs index 39ef365f43..3e8fd751df 100644 --- a/test/pleroma/web/media_proxy/invalidation/script_test.exs +++ b/test/pleroma/web/media_proxy/invalidation/script_test.exs @@ -10,11 +10,14 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do test "it logs error when script is not found" do assert capture_log(fn -> - assert Invalidation.Script.purge( - ["http://example.com/media/example.jpg"], - script_path: "./example" - ) == {:error, "%ErlangError{original: :enoent}"} - end) =~ "Error while cache purge: %ErlangError{original: :enoent}" + assert {:error, msg} = + Invalidation.Script.purge( + ["http://example.com/media/example.jpg"], + script_path: "./example" + ) + + assert msg =~ ~r/%ErlangError{original: :enoent(, reason: nil)?}/ + end) =~ ~r/Error while cache purge: %ErlangError{original: :enoent(, reason: nil)?}/ capture_log(fn -> assert Invalidation.Script.purge( From ec80a1e405c7b1d893c08ea99e824f2c13719c3a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 22:35:08 +0200 Subject: [PATCH 24/45] Bump minimum Elixir version to 1.10 1.9 being end-of-life --- CHANGELOG.md | 1 + Dockerfile | 2 +- ci/Dockerfile | 2 +- docs/installation/generic_dependencies.include | 2 +- elixir_buildpack.config | 2 +- lib/pleroma/config/loader.ex | 15 ++------------- mix.exs | 2 +- restarter/mix.exs | 2 +- 8 files changed, 9 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0ef4e113..caa5d0cd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - MastoFE ### Changed +- **Breaking:** Elixir >=1.10 is now required (was >= 1.9) - Allow users to remove their emails if instance does not need email to register - Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation` - Updated the recommended pleroma.vcl configuration for Varnish to target Varnish 7.0+ diff --git a/Dockerfile b/Dockerfile index e68b7ea7c3..44fd3ebcc7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.9-alpine as build +FROM elixir:1.10-alpine as build COPY . . diff --git a/ci/Dockerfile b/ci/Dockerfile index e6a8b438c1..5929f832d0 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.9.4 +FROM elixir:1.10.4 RUN apt-get update &&\ apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include index 2dbd93e427..dcaacfdfda 100644 --- a/docs/installation/generic_dependencies.include +++ b/docs/installation/generic_dependencies.include @@ -1,7 +1,7 @@ ## Required dependencies * PostgreSQL 9.6+ -* Elixir 1.9+ +* Elixir 1.10+ * Erlang OTP 22.2+ * git * file / libmagic diff --git a/elixir_buildpack.config b/elixir_buildpack.config index 946408c12f..1102e71455 100644 --- a/elixir_buildpack.config +++ b/elixir_buildpack.config @@ -1,2 +1,2 @@ -elixir_version=1.9.4 +elixir_version=1.10.4 erlang_version=22.3.4.1 diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 015be3d8ed..bd85eccab5 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -19,21 +19,10 @@ defmodule Pleroma.Config.Loader do :tesla ] - if Code.ensure_loaded?(Config.Reader) do - @reader Config.Reader - - def read(path), do: @reader.read!(path) - else - # support for Elixir less than 1.9 - @reader Mix.Config - def read(path) do - path - |> @reader.eval!() - |> elem(0) - end - end + @reader Config.Reader @spec read(Path.t()) :: keyword() + def read(path), do: @reader.read!(path) @spec merge(keyword(), keyword()) :: keyword() def merge(c1, c2), do: @reader.merge(c1, c2) diff --git a/mix.exs b/mix.exs index 66390d1137..d196eb872c 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ def project do [ app: :pleroma, version: version("2.4.52"), - elixir: "~> 1.9", + elixir: "~> 1.10", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), elixirc_options: [warnings_as_errors: warnings_as_errors()], diff --git a/restarter/mix.exs b/restarter/mix.exs index 9f26f5f649..4bb9b76e21 100644 --- a/restarter/mix.exs +++ b/restarter/mix.exs @@ -5,7 +5,7 @@ def project do [ app: :restarter, version: "0.1.0", - elixir: "~> 1.8", + elixir: "~> 1.10", start_permanent: Mix.env() == :prod, deps: deps() ] From 80a2528fd10ca2d07b8d96258a19bd9a8ea747ec Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 3 Sep 2022 00:05:29 +0200 Subject: [PATCH 25/45] ci-base: Document building and pushing a new image --- ci/Dockerfile | 5 +++-- ci/README | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 ci/README diff --git a/ci/Dockerfile b/ci/Dockerfile index 5929f832d0..d39fd8d7bc 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,7 +1,8 @@ FROM elixir:1.10.4 +# Single RUN statement, otherwise intermediate images are created +# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run RUN apt-get update &&\ - apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ + apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ mix local.hex --force &&\ mix local.rebar --force - diff --git a/ci/README b/ci/README new file mode 100644 index 0000000000..3785adef1e --- /dev/null +++ b/ci/README @@ -0,0 +1,12 @@ +## Dependencies + +Assuming an AMD64 Alpine system, you're going to need the following packages +- `qemu qemu-openrc qemu-arm qemu-aarch64` for binfmt +- `docker-cli-buildx` for building the images + +## Setting up + +``` +docker login git.pleroma.social:5050 +doas rc-service qemu-binfmt start +``` From cd237d22f165edb84202154e4c6f6725f63df635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Fri, 26 Aug 2022 18:30:43 +0200 Subject: [PATCH 26/45] User: generate private keys on user creation This fixes a race condition bug where keys could be regenerated post-federation, causing activities and HTTP signatures from an user to be dropped due to key differences. --- lib/pleroma/signature.ex | 5 +- lib/pleroma/user.ex | 19 +++---- .../activity_pub/activity_pub_controller.ex | 52 +++++-------------- .../web/activity_pub/views/user_view.ex | 2 - lib/pleroma/web/federator.ex | 6 +-- lib/pleroma/web/web_finger.ex | 4 -- test/pleroma/user_test.exs | 19 +------ .../web/activity_pub/views/user_view_test.exs | 7 --- test/support/factory.ex | 6 ++- 9 files changed, 32 insertions(+), 88 deletions(-) diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index dbe6fd209f..a7b8f48aa6 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -59,9 +59,8 @@ def refetch_public_key(conn) do end end - def sign(%User{} = user, headers) do - with {:ok, %{keys: keys}} <- User.ensure_keys_present(user), - {:ok, private_key, _} <- Keys.keys_from_pem(keys) do + def sign(%User{keys: keys} = user, headers) do + with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers) end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a57295891d..85d3382cb4 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -711,6 +711,7 @@ def register_changeset_ldap(struct, params = %{password: password}) |> put_ap_id() |> unique_constraint(:ap_id) |> put_following_and_follower_and_featured_address() + |> put_private_key() end def register_changeset(struct, params \\ %{}, opts \\ []) do @@ -768,6 +769,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do |> put_ap_id() |> unique_constraint(:ap_id) |> put_following_and_follower_and_featured_address() + |> put_private_key() end def validate_not_restricted_nickname(changeset, field) do @@ -846,6 +848,11 @@ defp put_following_and_follower_and_featured_address(changeset) do |> put_change(:featured_address, featured) end + defp put_private_key(changeset) do + {:ok, pem} = Keys.generate_rsa_pem() + put_change(changeset, :keys, pem) + end + defp autofollow_users(user) do candidates = Config.get([:instance, :autofollowed_nicknames]) @@ -2086,6 +2093,7 @@ defp create_service_actor(uri, nickname) do follower_address: uri <> "/followers" } |> change + |> put_private_key() |> unique_constraint(:nickname) |> Repo.insert() |> set_cache() @@ -2351,17 +2359,6 @@ def get_mascot(%{mascot: mascot}) when is_nil(mascot) do } end - def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user} - - def ensure_keys_present(%User{} = user) do - with {:ok, pem} <- Keys.generate_rsa_pem() do - user - |> cast(%{keys: pem}, [:keys]) - |> validate_required([:keys]) - |> update_and_set_cache() - end - end - def get_ap_ids_by_nicknames(nicknames) do from(u in User, where: u.nickname in ^nicknames, diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index b8f63d69dc..1357c379c2 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -66,8 +66,7 @@ defp relay_active?(conn, _) do end def user(conn, %{"nickname" => nickname}) do - with %User{local: true} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -174,7 +173,6 @@ def relay_following(conn, _params) do def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_follows, true} <- {:show_follows, (for_user && for_user == user) || !user.hide_follows} do {page, _} = Integer.parse(page) @@ -192,8 +190,7 @@ def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "p end def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -213,7 +210,6 @@ def relay_followers(conn, _params) do def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_followers, true} <- {:show_followers, (for_user && for_user == user) || !user.hide_followers} do {page, _} = Integer.parse(page) @@ -231,8 +227,7 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "p end def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -245,8 +240,7 @@ def outbox( %{"nickname" => nickname, "page" => page?} = params ) when page? in [true, "true"] do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do # "include_poll_votes" is a hack because postgres generates inefficient # queries when filtering by 'Answer', poll votes will be hidden by the # visibility filter in this case anyway @@ -270,8 +264,7 @@ def outbox( end def outbox(conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -328,14 +321,10 @@ defp post_inbox_relayed_create(conn, params) do end defp represent_service_actor(%User{} = user, conn) do - with {:ok, user} <- User.ensure_keys_present(user) do - conn - |> put_resp_content_type("application/activity+json") - |> put_view(UserView) - |> render("user.json", %{user: user}) - else - nil -> {:error, :not_found} - end + conn + |> put_resp_content_type("application/activity+json") + |> put_view(UserView) + |> render("user.json", %{user: user}) end defp represent_service_actor(nil, _), do: {:error, :not_found} @@ -388,12 +377,10 @@ def read_inbox( def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{ "nickname" => nickname }) do - with {:ok, user} <- User.ensure_keys_present(user) do - conn - |> put_resp_content_type("application/activity+json") - |> put_view(UserView) - |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"}) - end + conn + |> put_resp_content_type("application/activity+json") + |> put_view(UserView) + |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"}) end def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{ @@ -530,19 +517,6 @@ defp set_requester_reachable(%Plug.Conn{} = conn, _) do conn end - defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do - {:ok, new_user} = User.ensure_keys_present(user) - - for_user = - if new_user != user and match?(%User{}, for_user) do - User.get_cached_by_nickname(for_user.nickname) - else - for_user - end - - {new_user, for_user} - end - def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do with {:ok, object} <- ActivityPub.upload( diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 52f6bb56d2..f69fca0759 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -34,7 +34,6 @@ def render("endpoints.json", %{user: %User{local: true} = _user}) do def render("endpoints.json", _), do: %{} def render("service.json", %{user: user}) do - {:ok, user} = User.ensure_keys_present(user) {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -71,7 +70,6 @@ def render("user.json", %{user: %User{nickname: "internal." <> _} = user}), do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname) def render("user.json", %{user: user}) do - {:ok, user} = User.ensure_keys_present(user) {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index e7feefc073..3be71c1b69 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -61,10 +61,8 @@ def perform(:publish_one, module, params) do def perform(:publish, activity) do Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) - with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]), - {:ok, actor} <- User.ensure_keys_present(actor) do - Publisher.publish(actor, activity) - end + %User{} = actor = User.get_cached_by_ap_id(activity.data["actor"]) + Publisher.publish(actor, activity) end def perform(:incoming_ap_doc, params) do diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 6cd9962ce1..77ff40f46e 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -63,8 +63,6 @@ defp gather_aliases(%User{} = user) do end def represent_user(user, "JSON") do - {:ok, user} = User.ensure_keys_present(user) - %{ "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "aliases" => gather_aliases(user), @@ -73,8 +71,6 @@ def represent_user(user, "JSON") do end def represent_user(user, "XML") do - {:ok, user} = User.ensure_keys_present(user) - aliases = user |> gather_aliases() diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index b4a49624aa..0dc45beb95 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -677,14 +677,14 @@ test "it blocks blacklisted email domains" do assert changeset.valid? end - test "it sets the password_hash and ap_id" do + test "it sets the password_hash, ap_id, private key and followers collection address" do changeset = User.register_changeset(%User{}, @full_user_data) assert changeset.valid? assert is_binary(changeset.changes[:password_hash]) + assert is_binary(changeset.changes[:keys]) assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) - assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" end @@ -2131,21 +2131,6 @@ test "Only includes users with no read notifications" do end end - describe "ensure_keys_present" do - test "it creates keys for a user and stores them in info" do - user = insert(:user) - refute is_binary(user.keys) - {:ok, user} = User.ensure_keys_present(user) - assert is_binary(user.keys) - end - - test "it doesn't create keys if there already are some" do - user = insert(:user, keys: "xxx") - {:ok, user} = User.ensure_keys_present(user) - assert user.keys == "xxx" - end - end - describe "get_ap_ids_by_nicknames" do test "it returns a list of AP ids for a given set of nicknames" do user = insert(:user) diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs index 5cbfd8ab77..5f03c019e6 100644 --- a/test/pleroma/web/activity_pub/views/user_view_test.exs +++ b/test/pleroma/web/activity_pub/views/user_view_test.exs @@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do test "Renders a user, including the public key" do user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) result = UserView.render("user.json", %{user: user}) @@ -55,7 +54,6 @@ test "Renders with emoji tags" do test "Does not add an avatar image if the user hasn't set one" do user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) result = UserView.render("user.json", %{user: user}) refute result["icon"] @@ -67,8 +65,6 @@ test "Does not add an avatar image if the user hasn't set one" do banner: %{"url" => [%{"href" => "https://somebanner"}]} ) - {:ok, user} = User.ensure_keys_present(user) - result = UserView.render("user.json", %{user: user}) assert result["icon"]["url"] == "https://someurl" assert result["image"]["url"] == "https://somebanner" @@ -89,7 +85,6 @@ test "renders AKAs" do describe "endpoints" do test "local users have a usable endpoints structure" do user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) result = UserView.render("user.json", %{user: user}) @@ -105,7 +100,6 @@ test "local users have a usable endpoints structure" do test "remote users have an empty endpoints structure" do user = insert(:user, local: false) - {:ok, user} = User.ensure_keys_present(user) result = UserView.render("user.json", %{user: user}) @@ -115,7 +109,6 @@ test "remote users have an empty endpoints structure" do test "instance users do not expose oAuth endpoints" do user = insert(:user, nickname: nil, local: true) - {:ok, user} = User.ensure_keys_present(user) result = UserView.render("user.json", %{user: user}) diff --git a/test/support/factory.ex b/test/support/factory.ex index efbf3df2e6..dc8a3d3d8c 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Factory do require Pleroma.Constants + alias Pleroma.Keys alias Pleroma.Object alias Pleroma.User @@ -28,6 +29,8 @@ def conversation_factory do end def user_factory(attrs \\ %{}) do + {:ok, pem} = Keys.generate_rsa_pem() + user = %User{ name: sequence(:name, &"Test テスト User #{&1}"), email: sequence(:email, &"user#{&1}@example.com"), @@ -39,7 +42,8 @@ def user_factory(attrs \\ %{}) do last_refreshed_at: NaiveDateTime.utc_now(), notification_settings: %Pleroma.User.NotificationSetting{}, multi_factor_authentication_settings: %Pleroma.MFA.Settings{}, - ap_enabled: true + ap_enabled: true, + keys: pem } urls = From cfb1bc967f857569d8d0088a40e1d16e5cbbeca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Mon, 5 Sep 2022 03:51:35 +0200 Subject: [PATCH 27/45] Migrations: generate unset user keys User keys are now generated on user creation instead of "when needed", to prevent race conditions in federation and a few other issues. This migration will generate keys missing for local users. --- ...0220905011454_generate_unset_user_keys.exs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 priv/repo/migrations/20220905011454_generate_unset_user_keys.exs diff --git a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs new file mode 100644 index 0000000000..43bc7100bd --- /dev/null +++ b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.GenerateUnsetUserKeys do + use Ecto.Migration + import Ecto.Query + alias Pleroma.Keys + alias Pleroma.Repo + alias Pleroma.User + + def change do + query = + from(u in User, + where: u.local == true, + where: is_nil(u.keys), + select: u + ) + + Repo.stream(query) + |> Enum.each(fn user -> + with {:ok, pem} <- Keys.generate_rsa_pem() do + Ecto.Changeset.cast(user, %{keys: pem}, [:keys]) + |> Repo.update() + end + end) + end +end From c6bc52391460079efe18f48ed72eb6fd22757ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 5 Sep 2022 20:22:58 +0200 Subject: [PATCH 28/45] Clarify `birthday_min_age` config description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- config/description.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/description.exs b/config/description.exs index c28447b371..3a2a652726 100644 --- a/config/description.exs +++ b/config/description.exs @@ -1005,7 +1005,8 @@ key: :birthday_min_age, type: :integer, description: - "Minimum required age for users to create account. Only used if birthday is required." + "Minimum required age (in days) for users to create account. Only used if birthday is required.", + suggestions: [6570] } ] }, From 3d32c92b373b4e6ae325d5d590351af11caf7cf5 Mon Sep 17 00:00:00 2001 From: weblate-extractor Date: Tue, 6 Sep 2022 16:45:08 +0000 Subject: [PATCH 29/45] Extract translatable strings --- priv/gettext/config_descriptions.pot | 48 +++++++++++++++++++++---- priv/gettext/errors.pot | 52 ++++++++++------------------ priv/gettext/static_pages.pot | 49 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/priv/gettext/config_descriptions.pot b/priv/gettext/config_descriptions.pot index 9021fbfab2..a8074ee64e 100644 --- a/priv/gettext/config_descriptions.pot +++ b/priv/gettext/config_descriptions.pot @@ -1720,12 +1720,6 @@ msgctxt "config description at :pleroma-:instance > :banner_upload_limit" msgid "File size limit of user's profile banners" msgstr "" -#, elixir-autogen, elixir-format -#: lib/pleroma/docs/translator.ex:5 -msgctxt "config description at :pleroma-:instance > :birthday_min_age" -msgid "Minimum required age for users to create account. Only used if birthday is required." -msgstr "" - #, elixir-autogen, elixir-format #: lib/pleroma/docs/translator.ex:5 msgctxt "config description at :pleroma-:instance > :birthday_required" @@ -6021,3 +6015,45 @@ msgstr "" msgctxt "config label at :pleroma-:instance > :short_description" msgid "Short description" msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:delete_context_objects" +msgid "`delete_context_objects` background migration settings" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:delete_context_objects > :fault_rate_allowance" +msgid "Max accepted rate of objects that failed in the migration. Any value from 0.0 which tolerates no errors to 1.0 which will enable the feature even if context object deletion failed for all records." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:delete_context_objects > :sleep_interval_ms" +msgid "Sleep interval between each chunk of processed records in order to decrease the load on the system (defaults to 0 and should be keep default on most instances)." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:instance > :birthday_min_age" +msgid "Minimum required age (in days) for users to create account. Only used if birthday is required." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:delete_context_objects" +msgid "Delete context objects" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:delete_context_objects > :fault_rate_allowance" +msgid "Fault rate allowance" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:delete_context_objects > :sleep_interval_ms" +msgid "Sleep interval ms" +msgstr "" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index 85854d23ef..274e5fe7f1 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -90,7 +90,7 @@ msgid "must be equal to %{number}" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api.ex:523 +#: lib/pleroma/web/common_api.ex:558 msgid "Account not found" msgstr "" @@ -121,12 +121,12 @@ msgid "Can't get favorites" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:482 +#: lib/pleroma/web/common_api/utils.ex:457 msgid "Cannot post an empty status without attachments" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:441 +#: lib/pleroma/web/common_api/utils.ex:445 msgid "Comment must be up to %{max_size} characters" msgstr "" @@ -157,13 +157,13 @@ msgid "Could not unrepeat" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api.ex:530 -#: lib/pleroma/web/common_api.ex:539 +#: lib/pleroma/web/common_api.ex:565 +#: lib/pleroma/web/common_api.ex:574 msgid "Could not update state" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:205 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207 msgid "Error." msgstr "" @@ -194,7 +194,7 @@ msgid "Invalid parameters" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:349 +#: lib/pleroma/web/common_api/utils.ex:353 msgid "Invalid password." msgstr "" @@ -213,11 +213,6 @@ msgstr "" msgid "Missing parameters" msgstr "" -#, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:477 -msgid "No such conversation" -msgstr "" - #, elixir-autogen, elixir-format #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:171 #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:197 @@ -226,7 +221,7 @@ msgid "No such permission_group" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:515 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:502 #: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 #: lib/pleroma/web/feed/tag_controller.ex:16 #: lib/pleroma/web/feed/user_controller.ex:69 @@ -245,7 +240,7 @@ msgstr "" #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:39 #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:51 #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:52 -#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:326 +#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:382 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 msgid "Record not found" msgstr "" @@ -264,7 +259,7 @@ msgid "The message visibility must be direct" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:492 +#: lib/pleroma/web/common_api/utils.ex:467 msgid "The status is over the character limit" msgstr "" @@ -301,22 +296,22 @@ msgid "Your login is missing a confirmed e-mail address" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:403 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390 msgid "can't read inbox of %{nickname} as %{as_nickname}" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:502 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:489 msgid "can't update outbox of %{nickname} as %{as_nickname}" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api.ex:475 +#: lib/pleroma/web/common_api.ex:510 msgid "conversation is already muted" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:521 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:508 msgid "error" msgstr "" @@ -523,6 +518,7 @@ msgstr "" #: lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6 #: lib/pleroma/web/pleroma_api/controllers/report_controller.ex:6 #: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/settings_controller.ex:6 #: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 #: lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex:6 #: lib/pleroma/web/static_fe/static_fe_controller.ex:6 @@ -551,7 +547,7 @@ msgid "You can't revoke your own admin/moderator status." msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:129 +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:131 msgid "authorization required for timeline view" msgstr "" @@ -572,29 +568,19 @@ msgid "User is not an admin." msgstr "" #, elixir-format -#: lib/pleroma/user/backup.ex:75 +#: lib/pleroma/user/backup.ex:73 msgid "Last export was less than a day ago" msgid_plural "Last export was less than %{days} days ago" msgstr[0] "" msgstr[1] "" #, elixir-autogen, elixir-format -#: lib/pleroma/user/backup.ex:93 -msgid "Backups require enabled email" -msgstr "" - -#, elixir-autogen, elixir-format -#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:434 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:421 msgid "Character limit (%{limit} characters) exceeded, contains %{length} characters" msgstr "" #, elixir-autogen, elixir-format -#: lib/pleroma/user/backup.ex:98 -msgid "Email is required" -msgstr "" - -#, elixir-autogen, elixir-format -#: lib/pleroma/web/common_api/utils.ex:507 +#: lib/pleroma/web/common_api/utils.ex:482 msgid "Too many attachments" msgstr "" diff --git a/priv/gettext/static_pages.pot b/priv/gettext/static_pages.pot index 8e1b1d9dbe..3c64f1a29c 100644 --- a/priv/gettext/static_pages.pot +++ b/priv/gettext/static_pages.pot @@ -83,6 +83,7 @@ msgid "Account followed!" msgstr "" #, elixir-autogen, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:7 #: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7 msgctxt "placeholder text for account id" msgid "Your account ID, e.g. lain@quitter.se" @@ -511,3 +512,51 @@ msgstr "" msgctxt "account archive email body - admin requested" msgid "

Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:

\n

%{download_url}

\n" msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:123 +msgctxt "remote follow error message - unknown error" +msgid "Something went wrong." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:67 +msgctxt "remote follow error message - user not found" +msgid "Could not find user" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:8 +msgctxt "status interact authorization button" +msgid "Interact" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:2 +msgctxt "status interact error" +msgid "Error: %{error}" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:95 +msgctxt "status interact error message - status not found" +msgid "Could not find status" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:144 +msgctxt "status interact error message - unknown error" +msgid "Something went wrong." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4 +msgctxt "status interact header" +msgid "Interacting with %{nickname}'s %{status_link}" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex:4 +msgctxt "status interact header - status link text" +msgid "status" +msgstr "" From 0b19625bfba0ef4a9a4c97bada981dfb5c1edbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sun, 11 Sep 2022 04:54:04 +0200 Subject: [PATCH 30/45] ObjectView: do not fetch an object for its ID Non-Create/Listen activities had their associated object field normalized and fetched, but only to use their `id` field, which is both slow and redundant. This also failed on Undo activities, which delete the associated object/activity in database. Undo activities will now render properly and database loads should improve ever so slightly. --- lib/pleroma/object.ex | 15 ++++++++++----- lib/pleroma/web/activity_pub/views/object_view.ex | 4 ++-- .../web/activity_pub/views/object_view_test.exs | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fee3f1842b..38accae5d7 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -144,7 +144,7 @@ defp warn_on_no_object_preloaded(ap_id) do Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") end - def normalize(_, options \\ [fetch: false]) + def normalize(_, options \\ [fetch: false, id_only: false]) # If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # Use this whenever possible, especially when walking graphs in an O(N) loop! @@ -172,10 +172,15 @@ def normalize(%Activity{data: %{"object" => ap_id}}, options) do def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options) def normalize(ap_id, options) when is_binary(ap_id) do - if Keyword.get(options, :fetch) do - Fetcher.fetch_object_from_id!(ap_id, options) - else - get_cached_by_ap_id(ap_id) + cond do + Keyword.get(options, :id_only) -> + ap_id + + Keyword.get(options, :fetch) -> + Fetcher.fetch_object_from_id!(ap_id, options) + + true -> + get_cached_by_ap_id(ap_id) end end diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index f848aba3a5..63caa915c0 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -29,11 +29,11 @@ def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} def render("object.json", %{object: %Activity{} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity, fetch: false) + object_id = Object.normalize(activity, id_only: true) additional = Transmogrifier.prepare_object(activity.data) - |> Map.put("object", object.data["id"]) + |> Map.put("object", object_id) Map.merge(base, additional) end diff --git a/test/pleroma/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs index 48a4b47c4e..d94878e31d 100644 --- a/test/pleroma/web/activity_pub/views/object_view_test.exs +++ b/test/pleroma/web/activity_pub/views/object_view_test.exs @@ -81,4 +81,18 @@ test "renders an announce activity" do assert result["object"] == object.data["id"] assert result["type"] == "Announce" end + + test "renders an undo announce activity" do + note = insert(:note_activity) + user = insert(:user) + + {:ok, announce} = CommonAPI.repeat(note.id, user) + {:ok, undo} = CommonAPI.unrepeat(note.id, user) + + result = ObjectView.render("object.json", %{object: undo}) + + assert result["id"] == undo.data["id"] + assert result["object"] == announce.data["id"] + assert result["type"] == "Undo" + end end From 6bdf451ce88646b18115c03361415a986d845c67 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 11 Sep 2022 20:14:58 +0100 Subject: [PATCH 31/45] Use set of pregenerated RSA keys Randomness is a huge resource sink, so let's just use a some that we made earlier --- test/fixtures/rsa_keys/key_1.pem | 27 +++++++++++++++++++++++++++ test/fixtures/rsa_keys/key_2.pem | 27 +++++++++++++++++++++++++++ test/fixtures/rsa_keys/key_3.pem | 27 +++++++++++++++++++++++++++ test/fixtures/rsa_keys/key_4.pem | 27 +++++++++++++++++++++++++++ test/fixtures/rsa_keys/key_5.pem | 27 +++++++++++++++++++++++++++ test/support/factory.ex | 12 ++++++++++-- 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/rsa_keys/key_1.pem create mode 100644 test/fixtures/rsa_keys/key_2.pem create mode 100644 test/fixtures/rsa_keys/key_3.pem create mode 100644 test/fixtures/rsa_keys/key_4.pem create mode 100644 test/fixtures/rsa_keys/key_5.pem diff --git a/test/fixtures/rsa_keys/key_1.pem b/test/fixtures/rsa_keys/key_1.pem new file mode 100644 index 0000000000..3da3575004 --- /dev/null +++ b/test/fixtures/rsa_keys/key_1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2gdPJM5bWarGZ6QujfQ296l1yEQohS5fdtnxYQc+RXuS1gqZ +R/jVGHG25o4tmwyCLClyREU1CBTOCQBsg+BSehXlxNR9fiB4KaVQW9MMNa2vhHuG +f7HLdILiC+SPPTV1Bi8LCpxJowiSpnFPP4BDDeRKib7nOxll9Ln9gEpUueKKabsQ +EQKCmEJYhIz/8g5R0Qz+6VjASdejDjTEdZbr/rwyldRRjIklyeZ3lBzB/c8/51wn +HT2Dt0r9NiapxYC3oNhbE2A+4FU9pZTqS8yc3KqWZAy74snaRO9QQSednKlOJpXP +V3vwWo5CxuSNLttV7zRcrqeYOkIVNF4dQ/bHzQIDAQABAoIBADTCfglnEj4BkF92 +IHnjdgW6cTEUJUYNMba+CKY1LYF85Mx85hi/gzmWEu95yllxznJHWUpiAPJCrpUJ +EDldaDf44pAd53xE+S8CvQ5rZNH8hLOnfKWb7aL1JSRBm9PxAq+LZL2dkkgsg+hZ +FRdFv3Q2IT9x/dyUSdLNyyVnV1dfoya/7zOFc7+TwqlofznzrlBgNoAe8Lb4AN/q +itormPxskqATiq11XtP4F6eQ556eRgHCBxmktx/rRDl6f9G9dvjRQOA2qZlHQdFq +kjOZsrvItL46LdVoLPOdCYG+3HFeKoDUR1NNXEkt66eqmEhLY4MgzGUT1wqXWk7N +XowZc9UCgYEA+L5h4PhANiY5Kd+PkRI8zTlJMv8hFqLK17Q0p9eL+mAyOgXjH9so +QutJf4wU+h6ESDxH+1tCjCN307uUqT7YnT2zHf3b6GcmA+t6ewxfxOY2nJ82HENq +hK1aodnPTvRRRqCGfrx9qUHRTarTzi+2u86zH+KoMHSiuzn4VpQhg4MCgYEA4GOL +1tLR9+hyfYuMFo2CtQjp3KpJeGNKEqc33vFD05xJQX+m5THamBv8vzdVlVrMh/7j +iV85mlA7HaaP+r5DGwtonw9bqY76lYRgJJprsS5lHcRnXsDmU4Ne8RdB3dHNsT5P +n4P6v8y4jaT638iJ/qLt4e8itOBlZwS//VIglm8CgYEA7KXD3RKRlHK9A7drkOs2 +6VBM8bWEN1LdhGYvilcpFyUZ49XiBVatcS0EGdKdym/qDgc7vElQgJ7ly4y0nGfs +EXy3whrYcrxfkG8hcZuOKXeUEWHvSuhgmKWMilr8PfN2t6jVDBIrwzGY/Tk+lPUT +9o1qITW0KZVtlI5MU6JOWB0CgYAHwwnETZibxbuoIhqfcRezYXKNgop2EqEuUgB5 +wsjA2igijuLcDMRt/JHan3RjbTekAKooR1X7w4i39toGJ2y008kzr1lRXTPH1kNp +ILpW767pv7B/s5aEDwhKuK47mRVPa0Nf1jXnSpKbu7g943b6ivJFnXsK3LRFQwHN +JnkgGwKBgGUleQVd2GPr1dkqLVOF/s2aNB/+h2b1WFWwq0YTnW81OLwAcUVE4p58 +3GQgz8PCsWbNdTb9yFY5fq0fXgi0+T54FEoZWH09DrOepA433llAwI6sq7egrFdr +kKQttZMzs6ST9q/IOF4wgqSnBjjTC06vKSkNAlXJz+LMvIRMeBr0 +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/rsa_keys/key_2.pem b/test/fixtures/rsa_keys/key_2.pem new file mode 100644 index 0000000000..7a8e8e670e --- /dev/null +++ b/test/fixtures/rsa_keys/key_2.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAwu0VqVGRVDW09V3zZ0+08K9HMKivIzIInO0xim3jbfVcg8r1 +sR7vNLorYAB6TDDlXYAWKx1OxUMZusbOigrpQd+5wy8VdCogDD7qk4bbZ+NjXkuD +ETzrQsGWUXe+IdeH8L0Zh0bGjbarCuA0qAeY1TEteGl+Qwo2dsrBUH7yKmWO6Mz9 +XfPshrIDOGo4QNyVfEBNGq2K9eRrQUHeAPcM2/qu4ZAZRK+VCifDZrF8ZNpoAsnS +R2mJDhOBUMvI/ZaxOc2ry4EzwcS4uBaM2wONkGWDaqO6jNAQflaX7vtzOAeJB7Dt +VKXUUcZAGN7uI3c2mG5IKGMhTYUtUdrzmqmtZwIDAQABAoIBAQCHBJfTf3dt4AGn +T9twfSp06MQj9UPS2i5THI0LONCm8qSReX0zoZzJZgbzaYFM0zWczUMNvDA6vR7O +XDTmM2acxW4zv6JZo3Ata0sqwuepDz1eLGnt/8dppxQK/ClL4bH8088h/6k6sgPJ +9cEjfpejXHwFgvT9VM6i/BBpRHVTXWuJqwpDtg+bleQNN3L3RapluDd7BGiKoCwQ +cCTKd+lxTu9gVJkbRTI/Jn3kV+rnedYxHTxVp5cU1qIabsJWBcdDz25mRHupxQsn +JbQR4+ZnRLeAsC6WJZtEJz2KjXgBaYroHbGZY3KcGW95ILqiCJoJJugbW1eABKnN +Q5k8XVspAoGBAPzGJBZuX3c0quorhMIpREmGq2vS6VCQwLhH5qayYYH1LiPDfpdq +69lOROxZodzLxBgTf5z/a5kBF+eNKvOqfZJeRTxmllxxO1MuJQuRLi/b7BHHLuyN +Eea+YwtehA0T0CbD2hydefARNDruor2BLvt/kt6qEoIFiPauTsMfXP39AoGBAMVp +8argtnB+vsk5Z7rpQ4b9gF5QxfNbA0Hpg5wUUdYrUjFr50KWt1iowj6AOVp/EYgr +xRfvOQdYODDH7R5cjgMbwvtpHo39Zwq7ewaiT1sJXnpGmCDVh+pdTHePC5OOXnxN +0USK3M4KjltjVqJo7xPPElgJvCejudD47mtHMaQzAoGBAIFQ/PVc0goyL55NVUXf +xse21cv7wtEsvOuKHT361FegD1LMmN7uHGq32BryYBSNSmzmzMqNAYbtQEV9uxOd +jVBsWg9kjFgOtcMAQIOCapahdExEEoWCRj49+H3AhN4L3Nl4KQWqqs9efdIIc8lv +ZZHU2lZ/u6g5HLDWzASW7wQhAoGAdERPRrqN+HdNWinrA9Q6JxjKL8IWs5rYsksb +biMxh5eAEwdf7oHhfd/2duUB4mCQLMjKjawgxEia33AAIS+VnBMPpQ5mJm4l79Y3 +QNL7Nbyw3gcRtdTM9aT5Ujj3MnJZB5C1PU8jeF4TNZOuBH0UwW/ld+BT5myxFXhm +wtvtSq0CgYEA19b0/7il4Em6uiLOmYUuqaUoFhUPqzjaS6OM/lRAw12coWv/8/1P +cwaNZHNMW9Me/bNH3zcOTz0lxnYp2BeRehjFYVPRuS1GU7uwqKtlL2wCPptTfAhN +aJWIplzUCTg786u+sdNZ0umWRuCLoUpsKTgP/yt4RglzEcfxAuBDljk= +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/rsa_keys/key_3.pem b/test/fixtures/rsa_keys/key_3.pem new file mode 100644 index 0000000000..fbd25c80f7 --- /dev/null +++ b/test/fixtures/rsa_keys/key_3.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA0GvzqZ3r78GLa7guGn+palKRLGru4D4jnriHgfrUAJrdLyZ5 +9d0zAA4qnS2L6YAMoPPBhBUtIV5e2sn1+rwTClWU3dm3FyBAeqdeIBKN+04AyrUc +HXYaZtOPJXCTeytzoSQE359Tq6+xwgoHlUWSWxQF51/z/PDQcUvqFjJqAtdiDchd +3CiFRtdjegyxXGnqvPmBix+vEjDytcVydfch+R1Twf6f5EL7a1jFVWNGcratYBEl +nqOWKI2fBu/WA8QlrcVW5zmtZo9aJ6IrFddQgQTxPk/mEHgCzv8tbCRI9TxiXeYH +YqxZFYBW40xbZQwGRjaYHJlIRYp9+TOynW9OZQIDAQABAoIBAQC97cIMDbdVsyAk +N6D70N5H35ofygqJGtdG6o3B6xuKuZVaREvbu4mgQUigF0Nqs5/OhJMSlGGeCOuT +oXug1Abd4gNY7++jCWb43tAtlfsAyaJ7FvPZ/SguEBhgW+hp07z5WWN/jSeoSuFI +G++xHcczbFm88XncRG8O78kQFTz5/DlQYkFXfbqpuS3BqxnrACpDCUfrUwZNYFIp +CUNq21jdifhHwlS0K3PX8A5HdOYeVnVHaE78LGE4oJVHwcokELv+PYqarWZq/a6L +vKU3yn2+4pj2WO490iGQaRKVM35vrtjdVxiWEIUiFc3Jg5fKZA3wuHXoF1N1DpPO +BO6Att55AoGBAP/nC2szmDcnU5Sh8LDeQbL+FpSBwOmFnmel5uqbjKnDzf9emPQu +NFUls1N9OGgyUq08TnmcY/7wLZzcu7Y9XOUURuYtx9nGRs4RmE2VEBhK1r7CkDIx +oOb+NtdqnPtQASAxCHszoGCFxpuV7UVoo2SRgc+M4ceX128arvBUtvdrAoGBANCA +RuO3eelkXaJoCeogEUVWXZ6QmPeYzbMD4vg2DM0ynUbReyuEIIhn+SR7tehlj5ie +4T3ixVdur6k+YUdiFhUYgXaHBJWHoHl1lrU3ZON8n7AeEk9ft6gg4L07ouj78UMZ +sArJIlU5mLnW02zbV9XryU39dIgpQREqC0bIOtVvAoGBAORv1JKq6Rt7ALJy6VCJ +5y4ogfGp7pLHk8NEpuERYDz/rLllMbbwNAk6cV17L8pb+c/pQMhwohcnQiCALxUc +q/tW4X+CqJ+vzu8PZ90Bzu9Qh2iceGpGQTNTBZPA+UeigI7DFqYcTPM9GDE1YiyO +nyUcezvSsI4i7s6gjD+/7+DnAoGABm3+QaV1z/m1XX3B2IN2pOG971bcML54kW2s +QSVBjc5ixT1OhBAGBM7YAwUBnhILtJQptAPbPBAAwMJYs5/VuH7R9zrArG/LRhOX +Oy1jIhTEw+SZgfMcscWZyJwfMPob/Yq8QAjl0yT8jbaPPIsjEUi9I3eOcWh8RjA6 +ussP7WcCgYEAm3yvJR9z6QGoQQwtDbwjyZPYOSgK9wFS/65aupi6cm/Qk2N1YaLY +q2amNrzNsIc9vQwYGEHUwogn4MieHk96V7m2f0Hx9EHCMwizU9EiS6oyiLVowTG6 +YsBgSzcpnt0Vkgil4CQks5uQoan0tubEUQ5DI79lLnb02n4o46iAYK0= +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/rsa_keys/key_4.pem b/test/fixtures/rsa_keys/key_4.pem new file mode 100644 index 0000000000..f72b29fb1c --- /dev/null +++ b/test/fixtures/rsa_keys/key_4.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAw6MLRbP/henX2JxwdMkQlskKghBoMyUPu9kZpUQ9yYfIm9I4 +a3gEfzef75jKLOSf+BkZulvEUGjC+VnkpV3s+OZCSq81Ykv5PHuTqbj8Cn/dEt/g +lBXxPcOBKWqa+1cDX6QVIVJsBihLB/1b64H3U96Yu9+knmXvT1Az5MFA2KtSq7HJ +O+GJNn0EMI7xwPz/atUGlMLrhzwS4UDpw9CAaRPojplJYl4K1JMCFTgTt3hJILXZ +tw1MKTeeyWzNiuQRBQJuCnqfvsBYsasIlHWfqIL/uBzcGHHCIK5ZW9luntJXyLVj +zzaF7etIJk1uddM2wnqOOaVyqbssZXGt7Tb9IQIDAQABAoIBAH5QJRUKFK8Xvp9C +0nD06NsSTtCPW1e6VCBLGf3Uw7f9DY9d+cOZp/2jooYGNnMp4gdD3ZKvcV8hZNGu +Mqx6qmhB8wdZfLRMrU1Z1Is+vqzgxZJMLiouyKXCNwDQreQd2DXGMUZkew62sUsl +UFYMge4KyL50tUr4Mb0Z4YePJxk804tcqgw0n+D0lR7ZKhSqoQpoMqEiO+27Yw7E +Txj/MKH8f/ZJ6LBLRISOdBOrxonHqqeYWchczykCwojOZc3bIlWZGhg727dFTHDC +yrj3/zsZ2hy+TQsucCFY0RljIbacmHvrF/VqfhTIhg98H0F27V/jiPGsdKhptyst +E9iQVMkCgYEA42ge4H2Wl42sRh61GOrOgzzr0WZS54bF5skMxiGGnLwnb82rwUBt +xw94PRORJbV9l+2fkxbfiW0uzornfN8OBHSB64Pcjzzbl5Qm+eaDOiuTLtakYOWQ +/ipGqw8iE4J9iRteZCo8GnMxWbTkYCporTlFDTeYguXmwR4yCXtlCbMCgYEA3DxM +7R5HMUWRe64ucdekMh742McS8q/X5jdN9iFGy0M8P1WTyspSlaPDXgjaO4XqpRqg +djkL993kCDvOAiDl6Tpdiu1iFcOaRLb19Tj1pm8sKdk6X4d10U9lFri4NVYCmvVi +yOahUYFK/k5bA+1o+KU9Pi82H36H3WNeF4evC9sCgYEAs1zNdc04uQKiTZAs0KFr +DzI+4aOuYjT35ObQr3mD/h2dkV6MSNmzfF1kPfAv/KkgjXN7+H0DBRbb40bF/MTF +/peSXZtcnJGote7Bqzu4Z2o1Ja1ga5jF+uKHaKZ//xleQIUYtzJkw4v18cZulrb8 +ZxyTrTAbl6sTjWBuoPH1qGcCgYEAsQNahR9X81dKJpGKTQAYvhw8wOfI5/zD2ArN +g62dXBRPYUxkPJM/q3xzs6oD1eG+BjQPktYpM3FKLf/7haRxhnLd6qL/uiR8Ywx3 +RkEg2EP0yDIMA+o5nSFmS8vuaxgVgf0HCBiuwnbcEuhhqRdxzp/pSIjjxI6LnzqV +zu3EmQ8CgYEAhq8Uhvw+79tK7q2PCjDbiucA0n/4a3aguuvRoEh7F93Pf6VGZmT+ +Yld54Cd4P5ATI3r5YdD+JBuvgNMOTVPCaD/WpjbJKnrpNEXtXRQD6LzAXZDNk0sF +IO9i4gjhBolRykWn10khoPdxw/34FWBP5SxU1JYk75NQXvI3TD+5xbU= +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/rsa_keys/key_5.pem b/test/fixtures/rsa_keys/key_5.pem new file mode 100644 index 0000000000..49342b54e5 --- /dev/null +++ b/test/fixtures/rsa_keys/key_5.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA0jdKtMkgqnEGO3dn4OKxtggfFDzv+ddXToO0cdPXkUgPajCo +UGPunz+A1KmkAmLY0Vwk0tkOmKK8GFHek/5zQ+1N2FHBi19fbwlJk7hzh5OiYRhu +YZi0d6LsqEMKhDk6NqIeiFmOe2YHgklVvZV0hebvHlHLgzDhYrDltSPe33UZa3MS +g2Knf4WQAjLOo2BAb+oyj/UNXeAqaMGcOr6/kAHPcODW2EGhF3H3umFLv7t/Kq5i +WPBgarbCGPR5qq9SW5ZIjS3Sz0dl105Grw8wU23CC/2IBZ5vNiu+bkmLEoh/KpX2 +YBILoLmwtVX0Qxc15CrpOi12p+/4pLR8kuEowQIDAQABAoIBAQDMDQ3AJMdHisSQ +7pvvyDzWRFXesDQE4YmG1gNOxmImTLthyW9n8UjMXbjxNOXVxxtNRdMcs8MeWECa +nsWeBEzgr7VzeBCV9/LL9kjsUgwamyzwcOWcaL0ssAJmZgUMSfx+0akvkzbiAyzg +w8ytZSihXYPYe28/ni/5O1sOFI6feenOnJ9NSmVUA24c9TTJGNQs7XRUMZ8f9wt6 +KwRmYeNDKyqH7NvLmmKoDp6m7bMDQxWArVTAoRWTVApnj35iLQtmSi8DBdw6xSzQ +fKpUe/B4iQmMNxUW7KmolOvCIS5wcYZJE+/j7xshA2GGnOpx4aC+N+w2GSX4Bz/q +OnYSpGUBAoGBAOwnSeg17xlZqmd86qdiCxg0hRtAjwrd7btYq6nkK+t9woXgcV99 +FBS3nLbk/SIdXCW8vHFJTmld60j2q2kdestYBdHznwNZJ4Ee8JhamzcC64wY7O0x +RameO/6uoKS4C3VF+Zc9CCPfZOqYujkGvSqbTjFZWuFtDp0GHDk+qEIRAoGBAOPh ++PCB2QkGgiujSPmuCT5PTuNylAug3D4ZdMRKpQb9Rnzlia1Rpdrihq+PvB2vwa+S +mB6dgb0E7M2AyEMVu5buris0mVpRdmEeLCXR8mYJ48kOslIGArEStXDetfbRaXdK +7vf4APq2d78AQYldU2fYlo754Dh/3MZIguzpqMuxAoGBAIDJqG/AQiYkFV+c62ff +e0d3FQRYv+ngQE9Eu1HKwv0Jt7VFQu8din8F56yC013wfxmBhY+Ot/mUo8VF6RNJ +ZXdSCNKINzcfPwEW+4VLHIzyxbzAty1gCqrHRdbOK4PJb05EnCqTuUW/Bg0+v4hs +GWwMCKe3IG4CCM8vzuKVPjPRAoGBANYCQtJDb3q9ZQPsTb1FxyKAQprx4Lzm7c9Y +AsPRQhhFRaxHuLtPQU5FjK1VdBoBFAl5x2iBDPVhqa348pml0E0Xi/PBav9aH61n +M5i1CUrwoL4SEj9bq61133XHgeXwlnZUpgW0H99T+zMh32pMfea5jfNqETueQMzq +DiLF8SKRAoGBAOFlU0kRZmAx3Y4rhygp1ydPBt5+zfDaGINRWEN7QWjhX2QQan3C +SnXZlP3POXLessKxdCpBDq/RqVQhLea6KJMfP3F0YbohfWHt96WjiriJ0d0ZYVhu +34aUM2UGGG0Kia9OVvftESBaXk02vrY9zU3LAVAv0eLgIADm1kpj85v7 +-----END RSA PRIVATE KEY----- diff --git a/test/support/factory.ex b/test/support/factory.ex index c54d65b62c..09f02458cf 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -7,10 +7,18 @@ defmodule Pleroma.Factory do require Pleroma.Constants - alias Pleroma.Keys alias Pleroma.Object alias Pleroma.User + @rsa_keys [ + "test/fixtures/rsa_keys/key_1.pem", + "test/fixtures/rsa_keys/key_2.pem", + "test/fixtures/rsa_keys/key_3.pem", + "test/fixtures/rsa_keys/key_4.pem", + "test/fixtures/rsa_keys/key_5.pem" + ] + |> Enum.map(&File.read!/1) + def participation_factory do conversation = insert(:conversation) user = insert(:user) @@ -29,7 +37,7 @@ def conversation_factory do end def user_factory(attrs \\ %{}) do - {:ok, pem} = Keys.generate_rsa_pem() + pem = Enum.random(@rsa_keys) user = %User{ name: sequence(:name, &"Test テスト User #{&1}"), From ea60c4e7097c69df2023f23f60451f69668394f8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 14 Sep 2022 20:24:04 -0400 Subject: [PATCH 32/45] Fix wrong relationship direction --- .../controllers/account_controller.ex | 2 +- .../controllers/account_controller_test.exs | 17 ++++++++++++++++- 2 files changed, 17 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 50dd0e4c2e..2b736e5a32 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -481,7 +481,7 @@ def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _p def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do - render(conn, "relationship.json", user: follower, target: followed) + render(conn, "relationship.json", user: followed, target: follower) else nil -> render_error(conn, :not_found, "Record not found") 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 8311ebff93..b4e2a3081b 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1985,7 +1985,22 @@ test "removing user from followers", %{conn: conn, user: user} do CommonAPI.follow(other_user, user) - assert %{"id" => other_user_id, "followed_by" => false} = + assert %{"id" => ^other_user_id, "followed_by" => false} = + conn + |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers") + |> json_response_and_validate_schema(200) + + refute User.following?(other_user, user) + end + + test "removing remote user from followers", %{conn: conn, user: user} do + %{id: other_user_id} = other_user = insert(:user, local: false) + + CommonAPI.follow(other_user, user) + + assert User.following?(other_user, user) + + assert %{"id" => ^other_user_id, "followed_by" => false} = conn |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers") |> json_response_and_validate_schema(200) From 7f63b4c315653b4ed35afa326fc194feec21aea3 Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 15 Sep 2022 22:38:35 +0200 Subject: [PATCH 33/45] User: search: exclude deactivated users from user search This way we don't pollute search results with deactivated and deleted users --- lib/pleroma/user/search.ex | 5 +++++ test/pleroma/user_search_test.exs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index cd6f69f563..a7fb8fb833 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -94,6 +94,7 @@ defp search_query(query_string, for_user, following, top_user_ids) do |> subquery() |> order_by(desc: :search_rank) |> maybe_restrict_local(for_user) + |> filter_deactivated_users() end defp select_top_users(query, top_user_ids) do @@ -166,6 +167,10 @@ defp filter_internal_users(query) do from(q in query, where: q.actor_type != "Application") end + defp filter_deactivated_users(query) do + from(q in query, where: q.is_active == true) + end + defp filter_blocked_user(query, %User{} = blocker) do query |> join(:left, [u], b in Pleroma.UserRelationship, diff --git a/test/pleroma/user_search_test.exs b/test/pleroma/user_search_test.exs index 9b94f421dd..1deab6888b 100644 --- a/test/pleroma/user_search_test.exs +++ b/test/pleroma/user_search_test.exs @@ -65,6 +65,14 @@ test "excludes invisible users from results" do assert found_user.id == user.id end + test "excludes deactivated users from results" do + user = insert(:user, %{nickname: "john t1000"}) + insert(:user, %{is_active: false, nickname: "john t800"}) + + [found_user] = User.search("john") + assert found_user.id == user.id + end + # Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability test "includes non-discoverable users in results" do insert(:user, %{nickname: "john 3000", is_discoverable: false}) From 467b6cad6fce69d64c88342c3cd94eb05955441a Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 17 Sep 2022 16:34:33 -0400 Subject: [PATCH 34/45] Reduce incoming and outgoing federation queue sizes to 5 --- config/config.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index e07c3c7797..4e21ce457c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -559,8 +559,8 @@ token_expiration: 5, filter_expiration: 1, backup: 1, - federator_incoming: 50, - federator_outgoing: 50, + federator_incoming: 5, + federator_outgoing: 5, ingestion_queue: 50, web_push: 50, mailer: 10, From e66c02b77516758dda14a9b015f1e5d0b28b93b9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 20 Sep 2022 12:34:10 -0400 Subject: [PATCH 35/45] Make instance document controller test sync --- .../admin_api/controllers/instance_document_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs index 2601a026fb..9511dcceaa 100644 --- a/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase import Pleroma.Factory @dir "test/tmp/instance_static" From ecd2d32dc2851819a0b88f6d60553ef30b529344 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 28 Sep 2022 17:39:19 -0500 Subject: [PATCH 36/45] ci/Dockerfile: tabs to spaces --- ci/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/Dockerfile b/ci/Dockerfile index dcb2d0ab23..6f4f0d5b3f 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -3,6 +3,6 @@ FROM elixir:1.12 # Single RUN statement, otherwise intermediate images are created # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run RUN apt-get update &&\ - apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ - mix local.hex --force &&\ - mix local.rebar --force + apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ + mix local.hex --force &&\ + mix local.rebar --force From 5dfb582bd23986b09a09549e0e9d6e42de17a063 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 28 Sep 2022 17:41:36 -0500 Subject: [PATCH 37/45] Revert "Merge branch 'from/upstream-develop/tusooa/2169-queue-limit' into 'develop'" This reverts commit 757a21554f58c37319a283f06322c7a653bd680e, reversing changes made to 5d7d6233905f280c9d52cf1fc15e8f54280a3e58. --- config/config.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index 39a3f40ef2..9ba8896a63 100644 --- a/config/config.exs +++ b/config/config.exs @@ -571,8 +571,8 @@ token_expiration: 5, filter_expiration: 1, backup: 1, - federator_incoming: 5, - federator_outgoing: 5, + federator_incoming: 50, + federator_outgoing: 50, ingestion_queue: 50, web_push: 50, mailer: 10, From 91370898957a78f925045c5837fb27f8bcde2eb8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 28 Sep 2022 17:42:57 -0500 Subject: [PATCH 38/45] CI Docker: remove README, adapt build_and_push.sh --- ci/README | 12 ------------ ci/build_and_push.sh | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 ci/README diff --git a/ci/README b/ci/README deleted file mode 100644 index 3785adef1e..0000000000 --- a/ci/README +++ /dev/null @@ -1,12 +0,0 @@ -## Dependencies - -Assuming an AMD64 Alpine system, you're going to need the following packages -- `qemu qemu-openrc qemu-arm qemu-aarch64` for binfmt -- `docker-cli-buildx` for building the images - -## Setting up - -``` -docker login git.pleroma.social:5050 -doas rc-service qemu-binfmt start -``` diff --git a/ci/build_and_push.sh b/ci/build_and_push.sh index 484cc2643b..d0adca698e 100755 --- a/ci/build_and_push.sh +++ b/ci/build_and_push.sh @@ -1 +1 @@ -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:latest --push . +docker build -t gitlab.com/soapbox-pub/rebased/ci:latest --push . From f6afe649fecfc77090bbb0a6e97db2ff23ade90f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 28 Sep 2022 17:58:25 -0500 Subject: [PATCH 39/45] GenerateUnsetUserKeys: only select necessary user fields --- .../repo/migrations/20220905011454_generate_unset_user_keys.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs index 43bc7100bd..0d5330282b 100644 --- a/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs +++ b/priv/repo/migrations/20220905011454_generate_unset_user_keys.exs @@ -14,7 +14,7 @@ def change do from(u in User, where: u.local == true, where: is_nil(u.keys), - select: u + select: struct(u, [:id, :keys]) ) Repo.stream(query) From 0cd15c997c70ee2303c3c04e938b8c3de39a436c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 1 Oct 2022 19:56:11 -0500 Subject: [PATCH 40/45] TagValidator: allow Link tags, don't go nuclear for unrecognized Tag types --- .../object_validators/tag_validator.ex | 9 ++++ test/fixtures/fep-e232.json | 29 +++++++++++++ ..._01830912-1357-d4c5-e4a2-76eab347e749.json | 14 +++++++ .../web/activity_pub/transmogrifier_test.exs | 41 +++++++++++++++++++ test/support/http_request_mock.ex | 12 ++++++ 5 files changed, 105 insertions(+) create mode 100644 test/fixtures/fep-e232.json create mode 100644 test/fixtures/tesla_mock/mitra.social_01830912-1357-d4c5-e4a2-76eab347e749.json diff --git a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex index 9f15f19815..a059016c22 100644 --- a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do field(:url, ObjectValidators.Uri) end + field(:mediaType, ObjectValidators.MIME) field(:updated, ObjectValidators.DateTime) field(:id, ObjectValidators.Uri) end @@ -68,6 +69,14 @@ def changeset(struct, %{"type" => "Emoji"} = data) do |> validate_required([:type, :name, :icon]) end + def changeset(struct, %{"type" => "Link"} = data) do + struct + |> cast(data, [:type, :name, :href, :mediaType]) + end + + # Fallback + def changeset(struct, data), do: cast(struct, data, [:type, :name]) + def icon_changeset(struct, data) do struct |> cast(data, [:type, :url]) diff --git a/test/fixtures/fep-e232.json b/test/fixtures/fep-e232.json new file mode 100644 index 0000000000..0381efb505 --- /dev/null +++ b/test/fixtures/fep-e232.json @@ -0,0 +1,29 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://mitra.social/objects/01839574-d41f-01a7-8eef-abfe0badcd6a", + "type": "Note", + "attributedTo": "https://mitra.social/users/silverpill", + "content": "Quote test

RE: https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749

", + "published": "2022-10-01T21:30:05.211215Z", + "tag": [ + { + "name": "@silverpill@mitra.social", + "type": "Mention", + "href": "https://mitra.social/users/silverpill" + }, + { + "name": "RE: https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", + "type": "Link", + "href": "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", + "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://mitra.social/users/silverpill" + ], + "cc": [ + "https://mitra.social/users/silverpill/followers" + ], + "quoteUrl": "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749" +} diff --git a/test/fixtures/tesla_mock/mitra.social_01830912-1357-d4c5-e4a2-76eab347e749.json b/test/fixtures/tesla_mock/mitra.social_01830912-1357-d4c5-e4a2-76eab347e749.json new file mode 100644 index 0000000000..2f20bc7314 --- /dev/null +++ b/test/fixtures/tesla_mock/mitra.social_01830912-1357-d4c5-e4a2-76eab347e749.json @@ -0,0 +1,14 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", + "type": "Note", + "attributedTo": "https://mitra.social/users/silverpill", + "content": "FEP-e232 (Object Links) has been accepted into FEP repository:
\n
\nhttps://codeberg.org/fediverse/fep/src/branch/main/feps/fep-e232.md
\n
\nThis document has the \"DRAFT\" status. It is not finalized and still can be updated based on the feedback from implementers.", + "published": "2022-09-04T15:15:23.095610Z", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://mitra.social/users/silverpill/followers" + ] +} diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index b030e84dfe..3044a75ff8 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -130,6 +130,47 @@ test "it accepts quote posts" do assert Object.normalize("https://misskey.io/notes/8vs6wxufd0") end + test "it accepts FEP-e232 quote posts" do + insert(:user, ap_id: "https://mitra.social/users/silverpill") + + object = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!() + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "actor" => "https://mitra.social/users/silverpill", + "object" => object + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + # Object was created in the database + object = Object.normalize(activity) + + assert object.data["quoteUrl"] == + "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749" + + # The Link tag was normalized + assert object.data["tag"] == [ + %{ + "href" => "https://mitra.social/users/silverpill", + "name" => "@silverpill@mitra.social", + "type" => "Mention" + }, + %{ + "href" => "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", + "mediaType" => + "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "name" => + "RE: https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", + "type" => "Link" + } + ] + + # It fetched the quoted post + assert Object.normalize("https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749") + end + test "it fixes both the Create and object contexts in a reply" do insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8") insert(:user, ap_id: "https://p.helene.moe/users/helene") diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 1679e18ef1..eab8639aba 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -1389,6 +1389,18 @@ def get("https://misskey.io/notes/8vs6wxufd0", _, _, _) do }} end + def get("https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: + File.read!( + "test/fixtures/tesla_mock/mitra.social_01830912-1357-d4c5-e4a2-76eab347e749.json" + ), + headers: activitypub_object_headers() + }} + end + def get("https://gleasonator.com/objects/102eb097-a18b-4cd5-abfc-f952efcb70bb", _, _, _) do {:ok, %Tesla.Env{ From 4d9ecc2b9edf27b2e74416bb551d92415f5b8ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 6 Oct 2022 00:11:25 +0200 Subject: [PATCH 41/45] Fix down() in AddUpdateToNotificationsEnum migration for Rebased MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../20220605185734_add_update_to_notifications_enum.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/priv/repo/migrations/20220605185734_add_update_to_notifications_enum.exs b/priv/repo/migrations/20220605185734_add_update_to_notifications_enum.exs index 0656c885f9..63be931f3d 100644 --- a/priv/repo/migrations/20220605185734_add_update_to_notifications_enum.exs +++ b/priv/repo/migrations/20220605185734_add_update_to_notifications_enum.exs @@ -10,7 +10,7 @@ def up do |> execute() end - # 20210717000000_add_poll_to_notifications_enum.exs + # 20220319000000_add_status_to_notifications_enum.exs def down do alter table(:notifications) do modify(:type, :string) @@ -37,7 +37,8 @@ def down do 'reblog', 'favourite', 'pleroma:report', - 'poll' + 'poll', + 'status' ) """ |> execute() From 23ab8e12b9f29244ebbad492799fcbb31620c8f9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 9 Oct 2022 17:25:23 +0000 Subject: [PATCH 42/45] GitLab CI: tag `dind` --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 11cb697cef..e7d967a6d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -142,6 +142,8 @@ docker: cache: {} services: - docker:20.10.17-dind + tags: + - dind # https://medium.com/devops-with-valentine/how-to-build-a-docker-image-and-push-it-to-the-gitlab-container-registry-from-a-gitlab-ci-pipeline-acac0d1f26df script: - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin From 11dfc2589a4a0f6f3f539c921d7dc39d2bfeaeb5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 9 Oct 2022 13:27:16 -0500 Subject: [PATCH 43/45] Add support for FEP-e232 quotes without quoteUrl --- .../web/activity_pub/transmogrifier.ex | 26 +++++++++++++++++++ test/fixtures/fep-e232.json | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b5796b3308..6f590f4ce1 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -204,8 +204,34 @@ def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do |> fix_quote_url(options) end + # FEP-e232 + # https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-e232.md + def fix_quote_url(%{"tag" => tags} = object, options) when is_list(tags) do + tags + |> Enum.find(&is_quote_tag/1) + |> case do + %{"href" => quote_url} -> + object + |> Map.put("quoteUrl", quote_url) + |> fix_quote_url(options) + + _ -> + object + end + end + def fix_quote_url(object, _options), do: object + defp is_quote_tag(%{ + "type" => "Link", + "mediaType" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "href" => href + }) + when is_binary(href), + do: true + + defp is_quote_tag(_object), do: false + defp prepare_in_reply_to(in_reply_to) do cond do is_bitstring(in_reply_to) -> diff --git a/test/fixtures/fep-e232.json b/test/fixtures/fep-e232.json index 0381efb505..ef4b5a2ae0 100644 --- a/test/fixtures/fep-e232.json +++ b/test/fixtures/fep-e232.json @@ -24,6 +24,5 @@ ], "cc": [ "https://mitra.social/users/silverpill/followers" - ], - "quoteUrl": "https://mitra.social/objects/01830912-1357-d4c5-e4a2-76eab347e749" + ] } From 147f6c4f6340eb8d44a3b154140517cb16a58852 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 9 Oct 2022 13:37:27 -0500 Subject: [PATCH 44/45] Tag erratic Move tests --- .../mastodon_api/controllers/notification_controller_test.exs | 1 + test/pleroma/web/mastodon_api/views/notification_view_test.exs | 1 + 2 files changed, 2 insertions(+) diff --git a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs index 57ef7e330f..1e2eeb535b 100644 --- a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs @@ -600,6 +600,7 @@ test "see notifications after muting user with notifications and with_muted para assert length(json_response_and_validate_schema(conn, 200)) == 1 end + @tag :erratic test "see move notifications" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) diff --git a/test/pleroma/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs index d3d74f5cd3..772e1dcb38 100644 --- a/test/pleroma/web/mastodon_api/views/notification_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/notification_view_test.exs @@ -144,6 +144,7 @@ test "Follow notification" do refute Repo.one(Notification) end + @tag :erratic test "Move notification" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) From 0ee413400f9fd442b489243aa6c9b62b1f2eeeee Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 9 Oct 2022 15:25:34 -0500 Subject: [PATCH 45/45] FEP-e232: support Link mediaType "application/activity+json" --- lib/pleroma/web/activity_pub/transmogrifier.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 6f590f4ce1..b2ba0cbbc8 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -222,12 +222,17 @@ def fix_quote_url(%{"tag" => tags} = object, options) when is_list(tags) do def fix_quote_url(object, _options), do: object + @object_media_types [ + "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "application/activity+json" + ] + defp is_quote_tag(%{ "type" => "Link", - "mediaType" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "mediaType" => media_type, "href" => href }) - when is_binary(href), + when is_binary(href) and media_type in @object_media_types, do: true defp is_quote_tag(_object), do: false