From 0af77b20c19240479ea287446cc1c96a67318b2e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Sep 2021 22:11:18 -0400 Subject: Implement moving account Ref: emit-move --- .../api_spec/operations/twitter_util_operation.ex | 35 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 1 + .../web/twitter_api/controllers/util_controller.ex | 33 +++++++++++++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 2a701066d..4993058b5 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -214,6 +214,41 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } end + def move_account_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Move account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.move_account", + requestBody: request_body("Parameters", move_account_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{status: %Schema{type: :string, example: "success"}} + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp move_account_request do + %Schema{ + title: "MoveAccountRequest", + description: "POST body for moving the account", + type: :object, + required: [:password, :target_account], + properties: %{ + password: %Schema{type: :string, description: "Current password"}, + target_account: %Schema{ + type: :string, + description: "The nickname of the target account to move to" + } + } + } + end + def healthcheck_operation do %Operation{ tags: ["Accounts"], diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6defc8080..d4395dd7b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -343,6 +343,7 @@ defmodule Pleroma.Web.Router do post("/delete_account", UtilController, :delete_account) put("/notification_settings", UtilController, :update_notificaton_settings) post("/disable_account", UtilController, :disable_account) + post("/move_account", UtilController, :move_account) end scope "/api/pleroma", Pleroma.Web.PleromaAPI do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ccbef6d9f..3ca4c208c 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Emoji alias Pleroma.Healthcheck alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger @@ -26,7 +27,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do :change_password, :delete_account, :update_notificaton_settings, - :disable_account + :disable_account, + :move_account ] ) @@ -158,6 +160,35 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def move_account(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do + case CommonAPI.Utils.confirm_current_password(user, body_params.password) do + {:ok, user} -> + with {:ok, target_user} <- find_user_by_nickname(body_params.target_account), + {:ok, _user} <- ActivityPub.move(user, target_user) do + json(conn, %{status: "success"}) + else + {:not_found} -> + json(conn, %{error: "Target account not found."}) + + {:error, error} -> + json(conn, %{error: error}) + end + + {:error, msg} -> + json(conn, %{error: msg}) + end + end + + defp find_user_by_nickname(nickname) do + user = User.get_cached_by_nickname(nickname) + + if user == nil do + {:not_found, nil} + else + {:ok, user} + end + end + def captcha(conn, _params) do json(conn, Pleroma.Captcha.new()) end -- cgit v1.2.3 From 60081a88181e7af9b6110e9b7456e56f33c16f6b Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 09:40:20 -0400 Subject: Add User.add_alias/2 and User.alias_users/1 Ref: emit-move --- lib/pleroma/user.ex | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 390de1e2d..ff56927de 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2270,6 +2270,25 @@ defmodule Pleroma.User do |> update_and_set_cache() end + def alias_users(user) do + user.also_known_as + |> Enum.map(&User.get_cached_by_ap_id/1) + |> Enum.filter(fn user -> user != nil end) + end + + def add_alias(user, new_alias_user) do + current_aliases = user.also_known_as || [] + new_alias_ap_id = new_alias_user.ap_id + + if new_alias_ap_id in current_aliases do + {:ok, user} + else + user + |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as]) + |> update_and_set_cache() + end + end + # Internal function; public one is `deactivate/2` defp set_activation_status(user, status) do user -- cgit v1.2.3 From c1aa3c98ac923d0f1a032ef0e171ed4e27ae1453 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 11:46:37 -0400 Subject: Add get and add aliases endpoints Ref: emit-move --- .../api_spec/operations/twitter_util_operation.ex | 63 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 3 ++ .../web/twitter_api/controllers/util_controller.ex | 29 +++++++++- 3 files changed, 94 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 4993058b5..0fb54743d 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -249,6 +249,69 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } end + def list_aliases_operation do + %Operation{ + tags: ["Account credentials"], + summary: "List account aliases", + security: [%{"oAuth" => ["read:accounts"]}], + operationId: "UtilController.list_aliases", + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + aliases: %Schema{ + type: :array, + items: %Schema{type: :string}, + example: ["foo@example.org"] + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def add_alias_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Add an alias to this account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.add_alias", + requestBody: request_body("Parameters", add_alias_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + status: %Schema{ + type: :string, + example: "success" + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp add_alias_request do + %Schema{ + title: "AddAliasRequest", + description: "PUT body for adding aliases", + type: :object, + required: [:alias], + properties: %{ + alias: %Schema{ + type: :string, + description: "The nickname of the account to add to aliases" + } + } + } + end + def healthcheck_operation do %Operation{ tags: ["Accounts"], diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d4395dd7b..9ce0c9961 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -344,6 +344,9 @@ defmodule Pleroma.Web.Router do put("/notification_settings", UtilController, :update_notificaton_settings) post("/disable_account", UtilController, :disable_account) post("/move_account", UtilController, :move_account) + + put("/aliases", UtilController, :add_alias) + get("/aliases", UtilController, :list_aliases) end scope "/api/pleroma", Pleroma.Web.PleromaAPI do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 3ca4c208c..4c7d11e8d 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -28,7 +28,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do :delete_account, :update_notificaton_settings, :disable_account, - :move_account + :move_account, + :add_alias + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} + when action in [ + :list_aliases ] ) @@ -179,6 +188,24 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def add_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do + with {:ok, alias_user} <- find_user_by_nickname(body_params.alias), + {:ok, _user} <- user |> User.add_alias(alias_user) do + json(conn, %{status: "success"}) + else + {:error, error} -> + json(conn, %{error: error}) + end + end + + def list_aliases(%{assigns: %{user: user}} = conn, %{}) do + alias_nicks = user + |> User.alias_users() + |> Enum.map(&User.full_nickname/1) + + json(conn, %{aliases: alias_nicks}) + end + defp find_user_by_nickname(nickname) do user = User.get_cached_by_nickname(nickname) -- cgit v1.2.3 From 54d7b4354ce6d8da87831614855afc8e8dcc6aae Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 12:26:32 -0400 Subject: Add deleting alias endpoint Ref: emit-move --- lib/pleroma/user.ex | 13 +++++++ .../api_spec/operations/twitter_util_operation.ex | 40 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 1 + .../web/twitter_api/controllers/util_controller.ex | 21 ++++++++++-- 4 files changed, 73 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ff56927de..3c0de4f24 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2289,6 +2289,19 @@ defmodule Pleroma.User do end end + def delete_alias(user, alias_user) do + current_aliases = user.also_known_as || [] + alias_ap_id = alias_user.ap_id + + if alias_ap_id in current_aliases do + user + |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as]) + |> update_and_set_cache() + else + {:error, :no_such_alias} + end + end + # Internal function; public one is `deactivate/2` defp set_activation_status(user, status) do user diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 0fb54743d..3e915575c 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -312,6 +312,46 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } end + def delete_alias_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Delete an alias from this account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.delete_alias", + requestBody: request_body("Parameters", delete_alias_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + status: %Schema{ + type: :string, + example: "success" + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp delete_alias_request do + %Schema{ + title: "DeleteAliasRequest", + description: "PUT body for deleting aliases", + type: :object, + required: [:alias], + properties: %{ + alias: %Schema{ + type: :string, + description: "The nickname of the account to delete from aliases" + } + } + } + end + def healthcheck_operation do %Operation{ tags: ["Accounts"], diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9ce0c9961..4f97bb451 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -347,6 +347,7 @@ defmodule Pleroma.Web.Router do put("/aliases", UtilController, :add_alias) get("/aliases", UtilController, :list_aliases) + delete("/aliases", UtilController, :delete_alias) end scope "/api/pleroma", Pleroma.Web.PleromaAPI do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 4c7d11e8d..b3e16d527 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -29,7 +29,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do :update_notificaton_settings, :disable_account, :move_account, - :add_alias + :add_alias, + :delete_alias ] ) @@ -198,8 +199,24 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def delete_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do + with {:ok, alias_user} <- find_user_by_nickname(body_params.alias), + {:ok, _user} <- user |> User.delete_alias(alias_user) do + json(conn, %{status: "success"}) + else + {:error, :no_such_alias} -> + conn + |> put_status(404) + |> json(%{error: "Account has no such alias."}) + + {:error, error} -> + json(conn, %{error: error}) + end + end + def list_aliases(%{assigns: %{user: user}} = conn, %{}) do - alias_nicks = user + alias_nicks = + user |> User.alias_users() |> Enum.map(&User.full_nickname/1) -- cgit v1.2.3 From e41eee5ed1c4e7001a28dababe046e28357d2ffd Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 16:45:17 -0400 Subject: Make Move activity federate properly Ref: emit-move --- lib/pleroma/web/activity_pub/activity_pub.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 756096952..01dedd248 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -413,7 +413,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "type" => "Move", "actor" => origin.ap_id, "object" => origin.ap_id, - "target" => target.ap_id + "target" => target.ap_id, + "to" => [origin.follower_address] } with true <- origin.ap_id in target.also_known_as, -- cgit v1.2.3 From 4f44fd32eae100d5ce74b3c9bd5457858f145198 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 21:52:44 -0400 Subject: Federate unfollow activity in move_following properly 0: Use the CommonAPI unfollow function to make sure the unfollow activity is federated. 1: Limit the follow and unfollow to local followers only, while let the romote servers decide whether to move their followers. Ref: emit-move --- lib/pleroma/following_relationship.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index a0c7e6e39..61d36ce93 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -194,11 +194,12 @@ defmodule Pleroma.FollowingRelationship do |> join(:inner, [r], f in assoc(r, :follower)) |> where(following_id: ^origin.id) |> where([r, f], f.allow_following_move == true) + |> where([r, f], f.local == true) |> limit(50) |> preload([:follower]) |> Repo.all() |> Enum.map(fn following_relationship -> - Repo.delete(following_relationship) + Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) Pleroma.Web.CommonAPI.follow(following_relationship.follower, target) end) |> case do -- cgit v1.2.3 From a677c621e822673b3b2922d5b0975f704f2f59a7 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 15:30:04 -0400 Subject: Make move_following worker follow then unfollow Ref: emit-move --- lib/pleroma/following_relationship.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 61d36ce93..b101b9ee7 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -199,8 +199,8 @@ defmodule Pleroma.FollowingRelationship do |> preload([:follower]) |> Repo.all() |> Enum.map(fn following_relationship -> - Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) Pleroma.Web.CommonAPI.follow(following_relationship.follower, target) + Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) end) |> case do [] -> -- cgit v1.2.3 From eb383ef8d366c1656494278dfe6d2a6afdc04bc6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 16:26:22 -0400 Subject: Make move_account endpoint process non-existent users properly Ref: emit-move --- .../api_spec/operations/twitter_util_operation.ex | 3 ++- .../web/twitter_api/controllers/util_controller.ex | 23 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 3e915575c..fbaeb8da3 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -228,7 +228,8 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do properties: %{status: %Schema{type: :string, example: "success"}} }), 400 => Operation.response("Error", "application/json", ApiError), - 403 => Operation.response("Error", "application/json", ApiError) + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index b3e16d527..c076671d4 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -173,12 +173,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do def move_account(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do case CommonAPI.Utils.confirm_current_password(user, body_params.password) do {:ok, user} -> - with {:ok, target_user} <- find_user_by_nickname(body_params.target_account), + with {:ok, target_user} <- find_or_fetch_user_by_nickname(body_params.target_account), {:ok, _user} <- ActivityPub.move(user, target_user) do json(conn, %{status: "success"}) else - {:not_found} -> - json(conn, %{error: "Target account not found."}) + {:not_found, _} -> + conn + |> put_status(404) + |> json(%{error: "Target account not found."}) {:error, error} -> json(conn, %{error: error}) @@ -233,6 +235,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + defp find_or_fetch_user_by_nickname(nickname) do + user = User.get_by_nickname(nickname) + + if user != nil and user.local do + {:ok, user} + else + with {:ok, user} <- User.fetch_by_nickname(nickname) do + {:ok, user} + else + _ -> + {:not_found, nil} + end + end + end + def captcha(conn, _params) do json(conn, Pleroma.Captcha.new()) end -- cgit v1.2.3 From 9a27cb4f9d314fe1066f566de71357f55926116e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 19:27:04 -0400 Subject: Deal with target not found error in add_alias Ref: emit-move --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 3 ++- lib/pleroma/web/twitter_api/controllers/util_controller.ex | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index fbaeb8da3..4a2a246f5 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -293,7 +293,8 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } }), 400 => Operation.response("Error", "application/json", ApiError), - 403 => Operation.response("Error", "application/json", ApiError) + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index c076671d4..b8abc666e 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -196,6 +196,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do {:ok, _user} <- user |> User.add_alias(alias_user) do json(conn, %{status: "success"}) else + {:not_found, _} -> + conn + |> put_status(404) + |> json(%{error: "Target account does not exist."}) + {:error, error} -> json(conn, %{error: error}) end -- cgit v1.2.3 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: 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 --- .../web/api_spec/operations/account_operation.ex | 16 ++++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 19 ++++++++++++++++--- lib/pleroma/web/router.ex | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 026e92c5d..2a60cab78 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 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation 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 f15305f9c..31d75ba85 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do 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 ceb6c3cfd..8dc75b01e 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) -- cgit v1.2.3 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: 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 --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 31d75ba85..50dd0e4c2 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountController 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 + 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 -- cgit v1.2.3 From 31ff467eff9ef1b4bd645c8c868ac669e86bc140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 16 Mar 2022 20:59:02 +0100 Subject: Use `types` for filtering notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/notification_operation.ex | 6 ++++++ lib/pleroma/web/mastodon_api/controllers/notification_controller.ex | 2 +- lib/pleroma/web/mastodon_api/mastodon_api.ex | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index 7f2336ff6..56aa129d2 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -51,6 +51,12 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do :include_types, :query, %Schema{type: :array, items: notification_type()}, + "Deprecated, use `types` instead" + ), + Operation.parameter( + :types, + :query, + %Schema{type: :array, items: notification_type()}, "Include the notifications for activities with the given types" ), Operation.parameter( diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 932bc6423..740cf58e7 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -55,7 +55,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do def index(%{assigns: %{user: user}} = conn, params) do params = Map.new(params, fn {k, v} -> {to_string(k), v} end) - |> Map.put_new("include_types", @default_notification_types) + |> Map.put_new("types", Map.get(params, :include_types, @default_notification_types)) notifications = MastodonAPI.get_notifications(user, params) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 5e32b9611..b4d092eed 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -65,7 +65,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do user |> Notification.for_user_query(options) - |> restrict(:include_types, options) + |> restrict(:types, options) |> restrict(:exclude_types, options) |> restrict(:account_ap_id, options) |> Pagination.fetch_paginated(params) @@ -80,7 +80,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do defp cast_params(params) do param_types = %{ exclude_types: {:array, :string}, - include_types: {:array, :string}, + types: {:array, :string}, exclude_visibilities: {:array, :string}, reblogs: :boolean, with_muted: :boolean, @@ -92,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do changeset.changes end - defp restrict(query, :include_types, %{include_types: mastodon_types = [_ | _]}) do + defp restrict(query, :types, %{types: mastodon_types = [_ | _]}) do where(query, [n], n.type in ^mastodon_types) end -- cgit v1.2.3 From 4d482b765f8bebbad0d5e9e17fb923eb475313d6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 18:39:34 -0400 Subject: Allow to skip cache in Cache plug Ref: fix-local-public --- lib/pleroma/web/plugs/cache.ex | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index 111854859..e0467f107 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -98,14 +98,19 @@ defmodule Pleroma.Web.Plugs.Cache 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) - - opts.tracking_fun.(conn, tracking_fun_data) + cond do + Map.get(conn.assigns, :skip_cache, false) -> + conn + + !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") -- cgit v1.2.3 From fa3157df964d4f88d0fd1ce466a44333c8c7ef60 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 19:20:32 -0400 Subject: Skip cache when /objects or /activities is authenticated Ref: fix-local-public --- .../web/activity_pub/activity_pub_controller.ex | 11 +++++++++++ lib/pleroma/web/plugs/cache.ex | 21 ++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 57ac40b42..d423b1139 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 e0467f107..935b2d834 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -97,20 +97,23 @@ defmodule Pleroma.Web.Plugs.Cache do key = cache_key(conn, opts) content_type = content_type(conn) - conn = - cond do - Map.get(conn.assigns, :skip_cache, false) -> - conn + should_cache = not Map.get(conn.assigns, :skip_cache, false) - !opts[:tracking_fun] -> + conn = + unless opts[:tracking_fun] do + if should_cache do @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) - conn + end + + conn + else + tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) - true -> - 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") -- cgit v1.2.3 From c48be59f581fc6c3070a9d4cc889166b61981a6d Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 4 May 2022 22:51:40 -0400 Subject: Show local-only statuses in public timeline for authenticated users Ref: fix-local-public --- lib/pleroma/web/activity_pub/activity_pub.ex | 11 ++++++++++- .../web/mastodon_api/controllers/timeline_controller.ex | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 064f93b22..f8e840564 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -501,9 +501,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do + includes_local_public = Map.get(opts, :includes_local_public, false) + opts = Map.delete(opts, :user) - [Constants.as_public()] + intended_recipients = + if includes_local_public do + [Constants.as_public(), as_local_public()] + else + [Constants.as_public()] + end + + intended_recipients |> fetch_activities_query(opts) |> restrict_unlisted(opts) |> fetch_paginated_optimized(opts, pagination) diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index ba7239476..293c61b41 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -112,6 +112,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put(:muting_user, user) |> Map.put(:reply_filtering_user, user) |> Map.put(:instance, params[:instance]) + # Restricts unfederated content to authenticated users + |> Map.put(:includes_local_public, not is_nil(user)) |> ActivityPub.fetch_public_activities() conn -- cgit v1.2.3 From 826deb737588c75d9431d260eea826208100385c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 10:44:34 -0400 Subject: Make local-only statuses searchable Ref: fix-local-public --- lib/pleroma/activity/search.ex | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index 694dc5709..b56d4a5aa 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Activity.Search do Activity |> Activity.with_preloaded_object() |> Activity.restrict_deactivated_users() - |> restrict_public() + |> restrict_public(user) |> query_with(index_type, search_query, search_function) |> maybe_restrict_local(user) |> maybe_restrict_author(author) @@ -57,7 +57,16 @@ defmodule Pleroma.Activity.Search do def maybe_restrict_blocked(query, _), do: query - defp restrict_public(q) do + defp restrict_public(q, user) when not is_nil(user) do + intended_recipients = [Pleroma.Constants.as_public(), Pleroma.Web.ActivityPub.Utils.as_local_public()] + + from([a, o] in q, + where: fragment("?->>'type' = 'Create'", a.data), + where: fragment("? && ?", ^intended_recipients, a.recipients) + ) + end + + defp restrict_public(q, _user) do from([a, o] in q, where: fragment("?->>'type' = 'Create'", a.data), where: ^Pleroma.Constants.as_public() in a.recipients -- cgit v1.2.3 From 466568ae36fd247e635e5a1c4db2b5662eda1d02 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 11:18:18 -0400 Subject: Lint Ref: fix-local-public --- lib/pleroma/activity/search.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index b56d4a5aa..0b9b24aa4 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -58,7 +58,10 @@ defmodule Pleroma.Activity.Search do def maybe_restrict_blocked(query, _), do: query defp restrict_public(q, user) when not is_nil(user) do - intended_recipients = [Pleroma.Constants.as_public(), Pleroma.Web.ActivityPub.Utils.as_local_public()] + intended_recipients = [ + Pleroma.Constants.as_public(), + Pleroma.Web.ActivityPub.Utils.as_local_public() + ] from([a, o] in q, where: fragment("?->>'type' = 'Create'", a.data), -- cgit v1.2.3 From fe933b9bf2bd9787331db3a37e6bac472eace3d5 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 5 May 2022 18:07:30 -0400 Subject: Prevent remote access of local-only posts via /objects Ref: fix-local-public --- lib/pleroma/web/activity_pub/visibility.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 465f8a9b7..7c57f88f9 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -84,7 +84,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do when module in [Activity, Object] do x = [user.ap_id | User.following(user)] y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || []) - is_public?(message) || Enum.any?(x, &(&1 in y)) + + user_is_local = user.local + federatable = not is_local_public?(message) + (is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable) end def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do -- cgit v1.2.3 From 221cb3fb8125fac1757e1f1caeb36684d6c71050 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 May 2022 00:20:50 -0400 Subject: Allow users to create backups without providing email address Ref: backup-without-email --- lib/pleroma/user/backup.ex | 18 +----------------- lib/pleroma/workers/backup_worker.ex | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index 9cb329663..9df010605 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -32,9 +32,7 @@ defmodule Pleroma.User.Backup do end def create(user, admin_id \\ nil) do - with :ok <- validate_email_enabled(), - :ok <- validate_user_email(user), - :ok <- validate_limit(user, admin_id), + with :ok <- validate_limit(user, admin_id), {:ok, backup} <- user |> new() |> Repo.insert() do BackupWorker.process(backup, admin_id) end @@ -86,20 +84,6 @@ defmodule Pleroma.User.Backup do end end - defp validate_email_enabled do - if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do - :ok - else - {:error, dgettext("errors", "Backups require enabled email")} - end - end - - defp validate_user_email(%User{email: nil}) do - {:error, dgettext("errors", "Email is required")} - end - - defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok - def get_last(user_id) do __MODULE__ |> where(user_id: ^user_id) diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex index 3caef85b7..7657fa9ce 100644 --- a/lib/pleroma/workers/backup_worker.ex +++ b/lib/pleroma/workers/backup_worker.ex @@ -37,10 +37,7 @@ defmodule Pleroma.Workers.BackupWorker do backup_id |> Backup.get() |> Backup.process(), {:ok, _job} <- schedule_deletion(backup), :ok <- Backup.remove_outdated(backup), - {:ok, _} <- - backup - |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id) - |> Pleroma.Emails.Mailer.deliver() do + :ok <- maybe_deliver_email(backup, admin_user_id) do {:ok, backup} end end @@ -51,4 +48,23 @@ defmodule Pleroma.Workers.BackupWorker do nil -> :ok end end + + defp has_email?(user) do + not is_nil(user.email) and user.email != "" + end + + defp maybe_deliver_email(backup, admin_user_id) do + has_mailer = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) + backup = backup |> Pleroma.Repo.preload(:user) + + if has_email?(backup.user) and has_mailer do + backup + |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id) + |> Pleroma.Emails.Mailer.deliver() + + :ok + else + :ok + end + end end -- cgit v1.2.3 From 38444aa92a4ae89065c138f0f0110bef4fe48ace Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 9 May 2022 15:04:51 -0400 Subject: Allow authenticated users to access local-only posts in MastoAPI Ref: fix-local-public --- lib/pleroma/web/activity_pub/activity_pub.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index f8e840564..8e10edc24 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -612,9 +612,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do do: query defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do + local_public = as_local_public() from( a in query, - where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data) + where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public) ) end @@ -701,8 +702,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp user_activities_recipients(%{godmode: true}), do: [] defp user_activities_recipients(%{reading_user: reading_user}) do - if reading_user do - [Constants.as_public(), reading_user.ap_id | User.following(reading_user)] + if not is_nil(reading_user) and reading_user.local do + [Constants.as_public(), as_local_public(), reading_user.ap_id | User.following(reading_user)] else [Constants.as_public()] end -- cgit v1.2.3 From f1722a9f4a0a96c6a58fe25d57928c9843f96fc8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 9 May 2022 15:31:26 -0400 Subject: Make lint happy Ref: fix-local-public --- lib/pleroma/web/activity_pub/activity_pub.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8e10edc24..c28ea5e2f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -613,6 +613,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do local_public = as_local_public() + from( a in query, where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public) @@ -703,7 +704,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp user_activities_recipients(%{reading_user: reading_user}) do if not is_nil(reading_user) and reading_user.local do - [Constants.as_public(), as_local_public(), reading_user.ap_id | User.following(reading_user)] + [ + Constants.as_public(), + as_local_public(), + reading_user.ap_id | User.following(reading_user) + ] else [Constants.as_public()] end -- cgit v1.2.3 From 39c47073a3c6fd3da068d5a4c9def18f3847ff32 Mon Sep 17 00:00:00 2001 From: duponin Date: Wed, 18 May 2022 20:06:16 +0200 Subject: fix Ctrl-c catch on SSH BBS --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index a3b623bdf..47f5a920e 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -123,7 +123,7 @@ defmodule Pleroma.BBS.Handler do loop(%{state | counter: state.counter + 1}) - {:error, :interrupted} -> + {:input, ^input, {:error, :interrupted}} -> IO.puts("Caught Ctrl+C...") loop(%{state | counter: state.counter + 1}) -- cgit v1.2.3 From 5086d6d5e9ff68d6a7a82fd3ad6dbc0bad0b599c Mon Sep 17 00:00:00 2001 From: duponin Date: Thu, 19 May 2022 00:56:20 +0200 Subject: add thread show in BBS frontend --- lib/pleroma/bbs/handler.ex | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 47f5a920e..f1ac0c687 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -53,6 +53,7 @@ defmodule Pleroma.BBS.Handler do IO.puts("home - Show the home timeline") IO.puts("p - Post the given text") IO.puts("r - Reply to the post with the given id") + IO.puts("t - Show a thread from the given id") IO.puts("quit - Quit") state @@ -73,6 +74,33 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "t " <> activity_id) do + with %Activity{} = activity <- Activity.get_by_id(activity_id) do + activities = + ActivityPub.fetch_activities_for_context(activity.data["context"], %{ + blocking_user: user, + user: user, + exclude_id: activity.id + }) + + case activities do + [] -> + activity_id + |> Activity.get_by_id() + |> puts_activity() + + _ -> + activities + |> Enum.reverse() + |> Enum.each(&puts_activity/1) + end + else + _e -> IO.puts("An error occured when trying to show the thread...") + end + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) -- cgit v1.2.3 From b128e1d6c5bbc78874d05af2676550de80ae85c7 Mon Sep 17 00:00:00 2001 From: duponin Date: Thu, 19 May 2022 01:38:13 +0200 Subject: decode HTML to be human readable in BBS --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index f1ac0c687..c2491a20c 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -43,7 +43,7 @@ defmodule Pleroma.BBS.Handler do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - IO.puts(HTML.strip_tags(status.content)) + IO.puts(status.content |> HTML.strip_tags() |> HtmlEntities.decode()) IO.puts("") end -- cgit v1.2.3 From 33ced2c2ed9391ec95aae2205bb30d987ceac86d Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 04:17:34 +0200 Subject: BBS: put a new line for each HTML break in an activity Otherwise it would just put each line on the first one, which is not really readable --- lib/pleroma/bbs/handler.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index c2491a20c..d641de9ac 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -42,9 +42,14 @@ defmodule Pleroma.BBS.Handler do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) + IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - IO.puts(status.content |> HTML.strip_tags() |> HtmlEntities.decode()) - IO.puts("") + + status.content + |> String.split("
") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> Enum.map(&IO.puts/1) end def handle_command(state, "help") do -- cgit v1.2.3 From c04c7f9e45eec680afc0bf6c145fa55fc3f56ea8 Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 05:10:22 +0200 Subject: BBS: show notifactions --- lib/pleroma/bbs/handler.ex | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index d641de9ac..e0174efe1 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -52,6 +52,40 @@ defmodule Pleroma.BBS.Handler do |> Enum.map(&IO.puts/1) end + def puts_notification(activity, user) do + notification = + Pleroma.Web.MastodonAPI.NotificationView.render("show.json", %{ + notification: activity, + for: user + }) + + IO.puts( + "== (#{notification.type}) #{notification.status.id} by #{notification.account.display_name} (#{notification.account.acct})" + ) + + notification.status.content + |> String.split("
") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> (fn x -> + case x do + [content] -> + "> " <> content + + [head | _tail] -> + # "> " <> hd <> "..." + head + |> String.to_charlist() + |> Enum.take(80) + |> List.to_string() + |> (fn x -> "> " <> x <> "..." end).() + end + end).() + |> IO.puts() + + IO.puts("") + end + def handle_command(state, "help") do IO.puts("Available commands:") IO.puts("help - This help") @@ -59,6 +93,7 @@ defmodule Pleroma.BBS.Handler do IO.puts("p - Post the given text") IO.puts("r - Reply to the post with the given id") IO.puts("t - Show a thread from the given id") + IO.puts("n - Show notifications") IO.puts("quit - Quit") state @@ -106,6 +141,14 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "n") do + user + |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) + |> Enum.each(&puts_notification(&1, user)) + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) -- cgit v1.2.3 From e3e8ff06f9c588563003ba9855f2d38b9d6e08b7 Mon Sep 17 00:00:00 2001 From: duponin Date: Sat, 21 May 2022 05:10:48 +0200 Subject: BBS: mark notification as read --- lib/pleroma/bbs/handler.ex | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index e0174efe1..7314453af 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -94,6 +94,7 @@ defmodule Pleroma.BBS.Handler do IO.puts("r - Reply to the post with the given id") IO.puts("t - Show a thread from the given id") IO.puts("n - Show notifications") + IO.puts("n read - Mark all notifactions as read") IO.puts("quit - Quit") state @@ -141,6 +142,13 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "n read") do + Pleroma.Notification.clear(user) + IO.puts("All notifications are marked as read") + + state + end + def handle_command(%{user: user} = state, "n") do user |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) -- cgit v1.2.3 From a4659d993d1493406e9df4a26ada35cba50511c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sat, 21 May 2022 23:23:55 +0000 Subject: =?UTF-8?q?Apply=20H=C3=A9l=C3=A8ne=20suggestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pleroma/bbs/handler.ex | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 7314453af..a8f2fd37b 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -75,9 +75,7 @@ defmodule Pleroma.BBS.Handler do [head | _tail] -> # "> " <> hd <> "..." head - |> String.to_charlist() - |> Enum.take(80) - |> List.to_string() + |> String.slice(1, 80) |> (fn x -> "> " <> x <> "..." end).() end end).() @@ -136,7 +134,7 @@ defmodule Pleroma.BBS.Handler do |> Enum.each(&puts_activity/1) end else - _e -> IO.puts("An error occured when trying to show the thread...") + _e -> IO.puts("Could not show this thread...") end state @@ -144,7 +142,7 @@ defmodule Pleroma.BBS.Handler do def handle_command(%{user: user} = state, "n read") do Pleroma.Notification.clear(user) - IO.puts("All notifications are marked as read") + IO.puts("All notifications were marked as read") state end -- cgit v1.2.3 From fffd9059d67fb719c38dc014de1fa750dd5be8b4 Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 02:39:38 +0200 Subject: BBS: add post favourite feature --- lib/pleroma/bbs/handler.ex | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index a8f2fd37b..631307f02 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -93,6 +93,7 @@ defmodule Pleroma.BBS.Handler do IO.puts("t - Show a thread from the given id") IO.puts("n - Show notifications") IO.puts("n read - Mark all notifactions as read") + IO.puts("f - Favourites the post with the given id") IO.puts("quit - Quit") state @@ -167,6 +168,19 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "f " <> id) do + id = String.trim(id) + + with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, _activity} <- CommonAPI.favorite(user, activity) do + IO.puts("Favourited!") + else + _e -> IO.puts("Could not Favourite...") + end + + state + end + def handle_command(state, "home") do user = state.user -- cgit v1.2.3 From 5951d637a98402ad0e1d11d220c9374fc02d5bcd Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 02:40:56 +0200 Subject: BBS: show post ID when posted --- lib/pleroma/bbs/handler.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 631307f02..fecabb878 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -159,8 +159,8 @@ defmodule Pleroma.BBS.Handler do def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) - with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do - IO.puts("Posted!") + with {:ok, activity} <- CommonAPI.post(user, %{status: text}) do + IO.puts("Posted! ID: #{activity.id}") else _e -> IO.puts("Could not post...") end -- cgit v1.2.3 From 5ca1ac041f011df458af7ebe057b39c1cc9548d0 Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 22 May 2022 03:19:24 +0200 Subject: BBS: add repeat functionality --- lib/pleroma/bbs/handler.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index fecabb878..27799338f 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -94,6 +94,7 @@ defmodule Pleroma.BBS.Handler do IO.puts("n - Show notifications") IO.puts("n read - Mark all notifactions as read") IO.puts("f - Favourites the post with the given id") + IO.puts("R - Repeat the post with the given id") IO.puts("quit - Quit") state -- cgit v1.2.3 From 547def67a76854aa4c9c8438eb1ee4dfa36fd8ac Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 29 May 2022 11:36:00 -0400 Subject: Allow Updates by every actor on the same origin --- lib/pleroma/web/activity_pub/object_validators/update_validator.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex index a5def312e..1e940a400 100644 --- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex @@ -51,7 +51,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do with actor = get_field(cng, :actor), object = get_field(cng, :object), {:ok, object_id} <- ObjectValidators.ObjectID.cast(object), - true <- actor == object_id do + actor_uri <- URI.parse(actor), + object_uri <- URI.parse(object_id), + true <- actor_uri.host == object_uri.host do cng else _e -> -- cgit v1.2.3 From 0f6a5eb9a299629f295372f4d5ecdd9083a19717 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 29 May 2022 12:54:57 -0400 Subject: Handle Note and Question Updates --- lib/pleroma/web/activity_pub/side_effects.ex | 82 ++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index b997c15db..aeddf3ed8 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -153,23 +153,25 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # Tasks this handles: # - Update the user + # - Update a non-user object (Note, Question, etc.) # # For a local user, we also get a changeset with the full information, so we # can update non-federating, non-activitypub settings as well. @impl true def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do - if changeset = Keyword.get(meta, :user_update_changeset) do - changeset - |> User.update_and_set_cache() + updated_object_id = updated_object["id"] + + with {_, true} <- {:has_id, is_binary(updated_object_id)}, + {_, user} <- {:user, Pleroma.User.get_by_ap_id(updated_object_id)} do + if user do + handle_update_user(object, meta) + else + handle_update_object(object, meta) + end else - {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object) - - User.get_by_ap_id(updated_object["id"]) - |> User.remote_user_changeset(new_user_data) - |> User.update_and_set_cache() + _ -> + {:ok, object, meta} end - - {:ok, object, meta} end # Tasks this handles: @@ -390,6 +392,66 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end + defp handle_update_user( + %{data: %{"type" => "Update", "object" => updated_object}} = object, + meta + ) do + if changeset = Keyword.get(meta, :user_update_changeset) do + changeset + |> User.update_and_set_cache() + else + {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object) + + User.get_by_ap_id(updated_object["id"]) + |> User.remote_user_changeset(new_user_data) + |> User.update_and_set_cache() + end + + {:ok, object, meta} + end + + @updatable_object_types ["Note", "Question"] + # We do not allow poll options to be changed, but the poll description can be. + @updatable_fields [ + "source", + "tag", + "updated", + "emoji", + "content", + "summary", + "sensitive", + "attachment", + "generator" + ] + defp handle_update_object( + %{data: %{"type" => "Update", "object" => updated_object}} = object, + meta + ) do + orig_object = Object.get_by_ap_id(updated_object["id"]) + orig_object_data = orig_object.data + + if orig_object_data["type"] in @updatable_object_types do + updated_object_data = + @updatable_fields + |> Enum.reduce( + orig_object_data, + fn field, acc -> + if Map.has_key?(updated_object, field) do + Map.put(acc, field, updated_object[field]) + else + Map.drop(acc, [field]) + end + end + ) + + orig_object + |> Object.change(%{data: updated_object_data}) + |> Object.update_and_set_cache() + end + + {:ok, object, meta} + end + def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do actor = User.get_cached_by_ap_id(object.data["actor"]) -- cgit v1.2.3 From 5e8aac0e07cf54d527643e9793b92f3c0b3826e2 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 29 May 2022 13:54:16 -0400 Subject: Record edit history for Note and Question Updates --- lib/pleroma/web/activity_pub/side_effects.ex | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index aeddf3ed8..c4d56fa20 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -410,6 +410,26 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end + defp history_for_object(object) do + with history <- Map.get(object, "formerRepresentations"), + true <- is_map(history), + "OrderedCollection" <- Map.get(history, "type"), + true <- is_list(Map.get(history, "orderedItems")), + true <- is_integer(Map.get(history, "totalItems")) do + history + else + _ -> history_skeleton() + end + end + + defp history_skeleton do + %{ + "type" => "OrderedCollection", + "totalItems" => 0, + "orderedItems" => [] + } + end + @updatable_object_types ["Note", "Question"] # We do not allow poll options to be changed, but the poll description can be. @updatable_fields [ @@ -431,6 +451,19 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do orig_object_data = orig_object.data if orig_object_data["type"] in @updatable_object_types do + # Put edit history + # Note that we may have got the edit history by first fetching the object + history = history_for_object(orig_object_data) + + latest_history_item = + orig_object_data + |> Map.drop(["id", "formerRepresentations"]) + + new_history = + history + |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) + |> Map.put("totalItems", history["totalItems"] + 1) + updated_object_data = @updatable_fields |> Enum.reduce( @@ -443,6 +476,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end ) + |> Map.put("formerRepresentations", new_history) orig_object |> Object.change(%{data: updated_object_data}) -- cgit v1.2.3 From 8acfe95f3e9d4183fd513cfe828500c852db4d5f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 29 May 2022 22:16:03 -0400 Subject: Allow updating polls --- lib/pleroma/web/activity_pub/side_effects.ex | 81 +++++++++++++++++++++------- 1 file changed, 62 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index c4d56fa20..ac327280c 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -443,14 +443,29 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do "attachment", "generator" ] - defp handle_update_object( - %{data: %{"type" => "Update", "object" => updated_object}} = object, - meta - ) do - orig_object = Object.get_by_ap_id(updated_object["id"]) - orig_object_data = orig_object.data + defp update_content_fields(orig_object_data, updated_object) do + @updatable_fields + |> Enum.reduce( + %{data: orig_object_data, updated: false}, + fn field, %{data: data, updated: updated} -> + updated = updated or Map.get(updated_object, field) != Map.get(orig_object_data, field) + + data = + if Map.has_key?(updated_object, field) do + Map.put(data, field, updated_object[field]) + else + Map.drop(data, [field]) + end - if orig_object_data["type"] in @updatable_object_types do + %{data: data, updated: updated} + end + ) + end + + defp maybe_update_history(updated_object, orig_object_data, updated) do + if not updated do + updated_object + else # Put edit history # Note that we may have got the edit history by first fetching the object history = history_for_object(orig_object_data) @@ -464,19 +479,47 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) |> Map.put("totalItems", history["totalItems"] + 1) + updated_object + |> Map.put("formerRepresentations", new_history) + end + end + + defp maybe_update_poll(to_be_updated, updated_object) do + choice_key = fn data -> + if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf" + end + + with true <- to_be_updated["type"] == "Question", + key <- choice_key.(updated_object), + true <- key == choice_key.(to_be_updated), + orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])), + new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])), + true <- orig_choices == new_choices do + # Choices are the same, but counts are different + to_be_updated + |> Map.put(key, updated_object[key]) + else + # Choices (or vote type) have changed, do not allow this + _ -> to_be_updated + end + end + + defp handle_update_object( + %{data: %{"type" => "Update", "object" => updated_object}} = object, + meta + ) do + orig_object = Object.get_by_ap_id(updated_object["id"]) + orig_object_data = orig_object.data + + if orig_object_data["type"] in @updatable_object_types do + %{data: updated_object_data, updated: updated} = + orig_object_data + |> update_content_fields(updated_object) + updated_object_data = - @updatable_fields - |> Enum.reduce( - orig_object_data, - fn field, acc -> - if Map.has_key?(updated_object, field) do - Map.put(acc, field, updated_object[field]) - else - Map.drop(acc, [field]) - end - end - ) - |> Map.put("formerRepresentations", new_history) + updated_object_data + |> maybe_update_history(orig_object_data, updated) + |> maybe_update_poll(updated_object) orig_object |> Object.change(%{data: updated_object_data}) -- cgit v1.2.3 From c004eb0fa2c0a754a0fb839a961e35f406c57445 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 29 May 2022 23:50:31 -0400 Subject: Implement mastodon api for showing edit history --- lib/pleroma/object.ex | 20 ++++++ lib/pleroma/web/activity_pub/side_effects.ex | 22 +----- .../web/api_spec/operations/status_operation.ex | 82 ++++++++++++++++++++++ .../mastodon_api/controllers/status_controller.ex | 30 +++++++- lib/pleroma/web/mastodon_api/views/status_view.ex | 65 +++++++++++++++++ lib/pleroma/web/router.ex | 3 + 6 files changed, 199 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fe264b5e0..a893f2c1a 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -425,4 +425,24 @@ defmodule Pleroma.Object do end def object_data_hashtags(_), do: [] + + def history_for(object) do + with history <- Map.get(object, "formerRepresentations"), + true <- is_map(history), + "OrderedCollection" <- Map.get(history, "type"), + true <- is_list(Map.get(history, "orderedItems")), + true <- is_integer(Map.get(history, "totalItems")) do + history + else + _ -> history_skeleton() + end + end + + defp history_skeleton do + %{ + "type" => "OrderedCollection", + "totalItems" => 0, + "orderedItems" => [] + } + end end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index ac327280c..894c0ceef 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -410,26 +410,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end - defp history_for_object(object) do - with history <- Map.get(object, "formerRepresentations"), - true <- is_map(history), - "OrderedCollection" <- Map.get(history, "type"), - true <- is_list(Map.get(history, "orderedItems")), - true <- is_integer(Map.get(history, "totalItems")) do - history - else - _ -> history_skeleton() - end - end - - defp history_skeleton do - %{ - "type" => "OrderedCollection", - "totalItems" => 0, - "orderedItems" => [] - } - end - @updatable_object_types ["Note", "Question"] # We do not allow poll options to be changed, but the poll description can be. @updatable_fields [ @@ -468,7 +448,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do else # Put edit history # Note that we may have got the edit history by first fetching the object - history = history_for_object(orig_object_data) + history = Object.history_for(orig_object_data) latest_history_item = orig_object_data diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 639f24d49..e5322707f 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -6,9 +6,13 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.Attachment alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.Schemas.Poll alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -434,6 +438,29 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do } end + def show_history_operation do + %Operation{ + tags: ["Retrieve status history"], + summary: "Status history", + description: "View history of a status", + operationId: "StatusController.show_history", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + id_param() + ], + responses: %{ + 200 => status_history_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def show_source_operation do + end + + def update_operation do + end + def array_of_statuses do %Schema{type: :array, items: Status, example: [Status.schema().example]} end @@ -579,6 +606,61 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do Operation.response("Status", "application/json", Status) end + defp status_history_response do + Operation.response( + "Status History", + "application/json", + %Schema{ + title: "Status history", + description: "Response schema for history of a status", + type: :array, + items: %Schema{ + type: :object, + properties: %{ + account: %Schema{ + allOf: [Account], + description: "The account that authored this status" + }, + content: %Schema{ + type: :string, + format: :html, + description: "HTML-encoded status content" + }, + sensitive: %Schema{ + type: :boolean, + description: "Is this status marked as sensitive content?" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Subject or summary line, below which status content is collapsed until expanded" + }, + created_at: %Schema{ + type: :string, + format: "date-time", + description: "The date when this status was created" + }, + media_attachments: %Schema{ + type: :array, + items: Attachment, + description: "Media that is attached to this status" + }, + emojis: %Schema{ + type: :array, + items: Emoji, + description: "Custom emoji to be used when rendering status content" + }, + poll: %Schema{ + allOf: [Poll], + nullable: true, + description: "The poll attached to the status" + } + } + } + } + ) + end + defp context do %Schema{ title: "StatusContext", diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 42a95bdc5..72d85f1ec 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -38,7 +38,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do :index, :show, :card, - :context + :context, + :show_history, + :show_source ] ) @@ -49,7 +51,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do :create, :delete, :reblog, - :unreblog + :unreblog, + :update ] ) @@ -191,6 +194,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do create(%Plug.Conn{conn | body_params: params}, %{}) end + @doc "GET /api/v1/statuses/:id/history" + def show_history(%{assigns: %{user: user}} = conn, %{id: id} = params) do + with %Activity{} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do + try_render(conn, "history.json", + activity: activity, + for: user, + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) + ) + else + _ -> {:error, :not_found} + end + end + + @doc "GET /api/v1/statuses/:id/source" + def show_source(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do + end + + @doc "PUT /api/v1/statuses/:id" + def update(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do + end + @doc "GET /api/v1/statuses/:id" def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 1ebfd6740..c50e0d3da 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -384,6 +384,71 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do nil end + def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do + object = Object.normalize(activity, fetch: false) + + hashtags = Object.hashtags(object) + + user = CommonAPI.get_user(activity.data["actor"]) + + past_history = + Object.history_for(object.data) + |> Map.get("orderedItems") + |> Enum.map(&Map.put(&1, "id", object.data["id"])) + |> Enum.map(&%Object{data: &1, id: object.id}) + + history = [object | past_history] + + individual_opts = + opts + |> Map.put(:as, :object) + |> Map.put(:user, user) + |> Map.put(:hashtags, hashtags) + + render_many(history, StatusView, "history_item.json", individual_opts) + end + + def render( + "history_item.json", + %{activity: activity, user: user, object: object, hashtags: hashtags} = opts + ) do + sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw") + + attachment_data = object.data["attachment"] || [] + attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) + + created_at = Utils.to_masto_date(object.data["updated"] || object.data["published"]) + + content = + object + |> render_content() + + content_html = + content + |> Activity.HTML.get_cached_scrubbed_html_for_activity( + User.html_filter_policy(opts[:for]), + activity, + "mastoapi:content" + ) + + summary = object.data["summary"] || "" + + %{ + account: + AccountView.render("show.json", %{ + user: user, + for: opts[:for] + }), + content: content_html, + sensitive: sensitive, + spoiler_text: summary, + created_at: created_at, + media_attachments: attachments, + emojis: build_emojis(object.data["emoji"]), + poll: render(PollView, "show.json", object: object, for: opts[:for]) + } + end + def render("card.json", %{rich_media: rich_media, page_url: page_url}) do page_url_data = URI.parse(page_url) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ceb6c3cfd..2d2e5365e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -552,6 +552,9 @@ defmodule Pleroma.Web.Router do get("/bookmarks", StatusController, :bookmarks) post("/statuses", StatusController, :create) + get("/statuses/:id/history", StatusController, :show_history) + get("/statuses/:id/source", StatusController, :show_source) + put("/statuses/:id", StatusController, :update) delete("/statuses/:id", StatusController, :delete) post("/statuses/:id/reblog", StatusController, :reblog) post("/statuses/:id/unreblog", StatusController, :unreblog) -- cgit v1.2.3 From 393b50884607f9aca4d6e08bf429c8fe8f426f96 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 30 May 2022 00:59:23 -0400 Subject: Implement viewing source --- .../web/api_spec/operations/status_operation.ex | 36 ++++++++++++++++++++++ .../mastodon_api/controllers/status_controller.ex | 11 ++++++- lib/pleroma/web/mastodon_api/views/status_view.ex | 10 ++++++ 3 files changed, 56 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index e5322707f..617aba460 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -456,6 +456,20 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do end def show_source_operation do + %Operation{ + tags: ["Retrieve status source"], + summary: "Status source", + description: "View source of a status", + operationId: "StatusController.show_source", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + id_param() + ], + responses: %{ + 200 => status_source_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } end def update_operation do @@ -661,6 +675,28 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do ) end + defp status_source_response do + Operation.response( + "Status Source", + "application/json", + %Schema{ + type: :object, + properties: %{ + id: FlakeID, + text: %Schema{ + type: :string, + description: "Raw source of status content" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Subject or summary line, below which status content is collapsed until expanded" + } + } + } + ) + end + defp context do %Schema{ title: "StatusContext", diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 72d85f1ec..ea9e08aa8 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -210,7 +210,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/source" - def show_source(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do + def show_source(%{assigns: %{user: user}} = conn, %{id: id} = _params) do + with %Activity{} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do + try_render(conn, "source.json", + activity: activity, + for: user + ) + else + _ -> {:error, :not_found} + end end @doc "PUT /api/v1/statuses/:id" diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index c50e0d3da..8d4685ffa 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -449,6 +449,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do } end + def render("source.json", %{activity: %{data: %{"object" => _object}} = activity} = _opts) do + object = Object.normalize(activity, fetch: false) + + %{ + id: activity.id, + text: Map.get(object.data, "source", ""), + spoiler_text: Map.get(object.data, "summary", "") + } + end + def render("card.json", %{rich_media: rich_media, page_url: page_url}) do page_url_data = URI.parse(page_url) -- cgit v1.2.3 From b613a9ec6b68972c81dfe2f0175572bc7bd547f9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 31 May 2022 14:29:12 -0400 Subject: Implement mastodon api for editing status --- lib/pleroma/constants.ex | 24 ++++++++ lib/pleroma/web/activity_pub/builder.ex | 13 +++- lib/pleroma/web/activity_pub/side_effects.ex | 16 +---- .../web/api_spec/operations/status_operation.ex | 72 +++++++++++++++++++++- lib/pleroma/web/common_api.ex | 36 +++++++++++ .../mastodon_api/controllers/status_controller.ex | 21 ++++++- 6 files changed, 164 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index a42c71d23..bbb95104f 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -27,4 +27,28 @@ defmodule Pleroma.Constants do do: ~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css) ) + + const(status_updatable_fields, + do: [ + "source", + "tag", + "updated", + "emoji", + "content", + "summary", + "sensitive", + "attachment", + "generator" + ] + ) + + const(actor_types, + do: [ + "Application", + "Group", + "Organization", + "Person", + "Service" + ] + ) end diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 5b25138a4..532047599 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -218,10 +218,16 @@ defmodule Pleroma.Web.ActivityPub.Builder do end end - # Retricted to user updates for now, always public @spec update(User.t(), Object.t()) :: {:ok, map(), keyword()} def update(actor, object) do - to = [Pleroma.Constants.as_public(), actor.follower_address] + {to, cc} = + if object["type"] in Pleroma.Constants.actor_types() do + # User updates, always public + {[Pleroma.Constants.as_public(), actor.follower_address], []} + else + # Status updates, follow the recipients in the object + {object["to"] || [], object["cc"] || []} + end {:ok, %{ @@ -229,7 +235,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do "type" => "Update", "actor" => actor.ap_id, "object" => object, - "to" => to + "to" => to, + "cc" => cc }, []} end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 894c0ceef..49054c320 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Web.Streamer alias Pleroma.Workers.PollWorker + require Pleroma.Constants require Logger @cachex Pleroma.Config.get([:cachex, :provider], Cachex) @@ -411,20 +412,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end @updatable_object_types ["Note", "Question"] - # We do not allow poll options to be changed, but the poll description can be. - @updatable_fields [ - "source", - "tag", - "updated", - "emoji", - "content", - "summary", - "sensitive", - "attachment", - "generator" - ] defp update_content_fields(orig_object_data, updated_object) do - @updatable_fields + Pleroma.Constants.status_updatable_fields() |> Enum.reduce( %{data: orig_object_data, updated: false}, fn field, %{data: data, updated: updated} -> @@ -502,6 +491,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do |> maybe_update_poll(updated_object) orig_object + |> Repo.preload(:hashtags) |> Object.change(%{data: updated_object_data}) |> Object.update_and_set_cache() end diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 617aba460..c69307a4d 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -473,6 +473,22 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do end def update_operation do + %Operation{ + tags: ["Update status"], + summary: "Update status", + description: "Change the content of a status", + operationId: "StatusController.update", + security: [%{"oAuth" => ["write:statuses"]}], + parameters: [ + id_param() + ], + requestBody: request_body("Parameters", update_request(), required: true), + responses: %{ + 200 => status_response(), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } end def array_of_statuses do @@ -578,6 +594,60 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do } end + defp update_request do + %Schema{ + title: "StatusUpdateRequest", + type: :object, + properties: %{ + status: %Schema{ + type: :string, + nullable: true, + description: + "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided." + }, + media_ids: %Schema{ + nullable: true, + type: :array, + items: %Schema{type: :string}, + description: "Array of Attachment ids to be attached as media." + }, + poll: poll_params(), + sensitive: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Mark status and attached media as sensitive?" + }, + spoiler_text: %Schema{ + type: :string, + nullable: true, + description: + "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field." + }, + content_type: %Schema{ + type: :string, + nullable: true, + description: + "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint." + }, + to: %Schema{ + type: :array, + nullable: true, + items: %Schema{type: :string}, + description: + "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply" + } + }, + example: %{ + "status" => "What time is it?", + "sensitive" => "false", + "poll" => %{ + "options" => ["Cofe", "Adventure"], + "expires_in" => 420 + } + } + } + end + def poll_params do %Schema{ nullable: true, @@ -690,7 +760,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do spoiler_text: %Schema{ type: :string, description: - "Subject or summary line, below which status content is collapsed until expanded" + "Subject or summary line, below which status content is collapsed until expanded" } } } diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 1b95ee89c..e60c26053 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -402,6 +402,42 @@ defmodule Pleroma.Web.CommonAPI do end end + def update(user, orig_activity, changes) do + with orig_object <- Object.normalize(orig_activity), + {:ok, new_object} <- make_update_data(user, orig_object, changes), + {:ok, update_data, _} <- Builder.update(user, new_object), + {:ok, update, _} <- Pipeline.common_pipeline(update_data, local: true) do + {:ok, update} + else + _ -> {:error, nil} + end + end + + defp make_update_data(user, orig_object, changes) do + kept_params = %{ + visibility: Visibility.get_visibility(orig_object) + } + + params = Map.merge(changes, kept_params) + + with {:ok, draft} <- ActivityDraft.create(user, params) do + change = + Pleroma.Constants.status_updatable_fields() + |> Enum.reduce(orig_object.data, fn key, acc -> + if Map.has_key?(draft.object, key) do + acc |> Map.put(key, Map.get(draft.object, key)) + else + acc |> Map.drop([key]) + end + end) + |> Map.put("updated", Utils.make_date()) + + {:ok, change} + else + _ -> {:error, nil} + end + end + @spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()} def pin(id, %User{} = user) do with %Activity{} = activity <- create_activity_by_id(id), diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index ea9e08aa8..fa86e9dc0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -223,7 +223,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "PUT /api/v1/statuses/:id" - def update(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do + def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do + with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)}, + {_, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, + {_, true} <- {:is_create, activity.data["type"] == "Create"}, + actor <- Activity.user_actor(activity), + {_, true} <- {:own_status, actor.id == user.id}, + changes <- body_params |> put_application(conn), + {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(user, activity, changes)}, + {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do + try_render(conn, "show.json", + activity: activity, + for: user, + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) + ) + else + {:own_status, _} -> {:error, :forbidden} + {:pipeline, _} -> {:error, :internal_server_error} + _ -> {:error, :not_found} + end end @doc "GET /api/v1/statuses/:id" -- cgit v1.2.3 From 410e177b2ac3177f0645d7728b2ea922ba3c24d3 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 1 Jun 2022 12:02:03 -0400 Subject: Strip internal fields in formerRepresentation --- lib/pleroma/web/activity_pub/transmogrifier.ex | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a70330f0e..5750396a4 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -902,7 +902,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def strip_internal_fields(object) do - Map.drop(object, Pleroma.Constants.object_internal_fields()) + outer = Map.drop(object, Pleroma.Constants.object_internal_fields()) + + case outer do + %{"formerRepresentations" => %{"orderedItems" => list}} when is_list(list) -> + update_in( + outer["formerRepresentations"]["orderedItems"], + &Enum.map( + &1, + fn + item when is_map(item) -> Map.drop(item, Pleroma.Constants.object_internal_fields()) + item -> item + end + ) + ) + + _ -> + outer + end end defp strip_internal_tags(%{"tag" => tags} = object) do -- cgit v1.2.3 From fa31ae50e6ec44a3921a60d2a6c19e864f0511e7 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 1 Jun 2022 19:30:50 -0400 Subject: Inject history when object is refetched --- lib/pleroma/object.ex | 22 ++++++++++++++++++++++ lib/pleroma/object/fetcher.ex | 27 +++++++++++++++++++++++++++ lib/pleroma/web/activity_pub/side_effects.ex | 24 +----------------------- 3 files changed, 50 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index a893f2c1a..670ab8743 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -445,4 +445,26 @@ defmodule Pleroma.Object do "orderedItems" => [] } end + + def maybe_update_history(updated_object, orig_object_data, updated) do + if not updated do + updated_object + else + # Put edit history + # Note that we may have got the edit history by first fetching the object + history = Object.history_for(orig_object_data) + + latest_history_item = + orig_object_data + |> Map.drop(["id", "formerRepresentations"]) + + new_history = + history + |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) + |> Map.put("totalItems", history["totalItems"] + 1) + + updated_object + |> Map.put("formerRepresentations", new_history) + end + end end diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index deb3dc711..ce816c1fc 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -26,8 +26,35 @@ defmodule Pleroma.Object.Fetcher do end defp maybe_reinject_internal_fields(%{data: %{} = old_data}, new_data) do + has_history? = fn + %{"formerRepresentations" => %{"orderedItems" => list}} when is_list(list) -> true + _ -> false + end + internal_fields = Map.take(old_data, Pleroma.Constants.object_internal_fields()) + remote_history_exists? = has_history?.(new_data) + + # If the remote history exists, we treat that as the only source of truth. + new_data = + if has_history?.(old_data) and not remote_history_exists? do + Map.put(new_data, "formerRepresentations", old_data["formerRepresentations"]) + else + new_data + end + + # If the remote does not have history information, we need to manage it ourselves + new_data = + if not remote_history_exists? do + changed? = + Pleroma.Constants.status_updatable_fields() + |> Enum.any?(fn field -> Map.get(old_data, field) != Map.get(new_data, field) end) + + new_data |> Object.maybe_update_history(old_data, changed?) + else + new_data + end + Map.merge(new_data, internal_fields) end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 49054c320..52a343de7 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -431,28 +431,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do ) end - defp maybe_update_history(updated_object, orig_object_data, updated) do - if not updated do - updated_object - else - # Put edit history - # Note that we may have got the edit history by first fetching the object - history = Object.history_for(orig_object_data) - - latest_history_item = - orig_object_data - |> Map.drop(["id", "formerRepresentations"]) - - new_history = - history - |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) - |> Map.put("totalItems", history["totalItems"] + 1) - - updated_object - |> Map.put("formerRepresentations", new_history) - end - end - defp maybe_update_poll(to_be_updated, updated_object) do choice_key = fn data -> if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf" @@ -487,7 +465,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do updated_object_data = updated_object_data - |> maybe_update_history(orig_object_data, updated) + |> Object.maybe_update_history(orig_object_data, updated) |> maybe_update_poll(updated_object) orig_object -- cgit v1.2.3 From 8bac8147d4079c0ba0a54753bbab904e46dadbfc Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 3 Jun 2022 21:15:17 -0400 Subject: Stream out edits --- lib/pleroma/web/activity_pub/activity_pub.ex | 13 ++++++++++-- lib/pleroma/web/activity_pub/side_effects.ex | 6 ++++++ lib/pleroma/web/streamer.ex | 14 +++++++++++++ lib/pleroma/web/views/streamer_view.ex | 31 ++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 064f93b22..179e6763b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -190,7 +190,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def notify_and_stream(activity) do Notification.create_notifications(activity) - conversation = create_or_bump_conversation(activity, activity.actor) + original_activity = + case activity do + %{data: %{"type" => "Update"}, object: %{data: %{"id" => id}}} -> + Activity.get_create_by_object_ap_id_with_object(id) + + _ -> + activity + end + + conversation = create_or_bump_conversation(original_activity, original_activity.actor) participations = get_participations(conversation) stream_out(activity) stream_out_participations(participations) @@ -256,7 +265,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @impl true def stream_out(%Activity{data: %{"type" => data_type}} = activity) - when data_type in ["Create", "Announce", "Delete"] do + when data_type in ["Create", "Announce", "Delete", "Update"] do activity |> Topics.get_activity_topics() |> Streamer.stream(activity) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 52a343de7..05f9b9bd9 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -472,6 +472,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do |> Repo.preload(:hashtags) |> Object.change(%{data: updated_object_data}) |> Object.update_and_set_cache() + + if updated do + object + |> Activity.normalize() + |> ActivityPub.notify_and_stream() + end end {:ok, object, meta} diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index ff7f62a1e..8b7fb985b 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -296,6 +296,20 @@ defmodule Pleroma.Web.Streamer do defp push_to_socket(_topic, %Activity{data: %{"type" => "Delete"}}), do: :noop + defp push_to_socket(topic, %Activity{data: %{"type" => "Update"}} = item) do + anon_render = StreamerView.render("status_update.json", item) + + Registry.dispatch(@registry, topic, fn list -> + Enum.each(list, fn {pid, auth?} -> + if auth? do + send(pid, {:render_with_user, StreamerView, "status_update.json", item}) + else + send(pid, {:text, anon_render}) + end + end) + end) + end + defp push_to_socket(topic, item) do anon_render = StreamerView.render("update.json", item) diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex index 16c2b7d61..797762d90 100644 --- a/lib/pleroma/web/views/streamer_view.ex +++ b/lib/pleroma/web/views/streamer_view.ex @@ -25,6 +25,22 @@ defmodule Pleroma.Web.StreamerView do |> Jason.encode!() end + def render("status_update.json", %Activity{} = activity, %User{} = user) do + activity = Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"]) + + %{ + event: "status.update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "show.json", + activity: activity, + for: user + ) + |> Jason.encode!() + } + |> Jason.encode!() + end + def render("notification.json", %Notification{} = notify, %User{} = user) do %{ event: "notification", @@ -51,6 +67,21 @@ defmodule Pleroma.Web.StreamerView do |> Jason.encode!() end + def render("status_update.json", %Activity{} = activity) do + activity = Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"]) + + %{ + event: "status.update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "show.json", + activity: activity + ) + |> Jason.encode!() + } + |> Jason.encode!() + end + def render("chat_update.json", %{chat_message_reference: cm_ref}) do # Explicitly giving the cmr for the object here, so we don't accidentally # send a later 'last_message' that was inserted between inserting this and -- cgit v1.2.3 From 3249ac1f12b69718cacc193c020e8bdccf167a9e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 3 Jun 2022 21:47:40 -0400 Subject: Show edited_at in MastodonAPI/show --- lib/pleroma/web/api_spec/schemas/status.ex | 6 ++++++ lib/pleroma/web/mastodon_api/views/status_view.ex | 11 +++++++++++ 2 files changed, 17 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 6e6e30315..f803caec2 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -73,6 +73,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do format: "date-time", description: "The date when this status was created" }, + edited_at: %Schema{ + type: :string, + format: "date-time", + nullable: true, + description: "The date when this status was last edited" + }, emojis: %Schema{ type: :array, items: Emoji, diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 8d4685ffa..4afba4b33 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -258,6 +258,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do created_at = Utils.to_masto_date(object.data["published"]) + edited_at = + with %{"updated" => updated} <- object.data, + date <- Utils.to_masto_date(updated), + true <- date != "" do + date + else + _ -> + nil + end + reply_to = get_reply_to(activity, opts) reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"]) @@ -346,6 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do content: content_html, text: opts[:with_source] && object.data["source"], created_at: created_at, + edited_at: edited_at, reblogs_count: announcement_count, replies_count: object.data["repliesCount"] || 0, favourites_count: like_count, -- cgit v1.2.3 From fe2d4778eee5e8b4fe24f8e1d16d1065e9430027 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 4 Jun 2022 12:56:56 -0400 Subject: Expose content type of status sources --- .../web/api_spec/operations/status_operation.ex | 4 ++++ lib/pleroma/web/common_api/activity_draft.ex | 5 +++- lib/pleroma/web/common_api/utils.ex | 2 +- lib/pleroma/web/mastodon_api/views/status_view.ex | 27 +++++++++++++++++++--- 4 files changed, 33 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index c69307a4d..e921128c7 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -761,6 +761,10 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do type: :string, description: "Subject or summary line, below which status content is collapsed until expanded" + }, + content_type: %Schema{ + type: :string, + description: "The content type of the source" } } } diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 7c21c8c3a..9af635da8 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -224,7 +224,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do object = note_data |> Map.put("emoji", emoji) - |> Map.put("source", draft.status) + |> Map.put("source", %{ + "content" => draft.status, + "mediaType" => Utils.get_content_type(draft.params[:content_type]) + }) |> Map.put("generator", draft.params[:generator]) %__MODULE__{draft | object: object} diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index ce850b038..4c6a26384 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -219,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> maybe_add_attachments(draft.attachments, attachment_links) end - defp get_content_type(content_type) do + def get_content_type(content_type) do if Enum.member?(Config.get([:instance, :allowed_post_formats]), content_type) do content_type else diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 4afba4b33..f798b2624 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -354,7 +354,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblog: nil, card: card, content: content_html, - text: opts[:with_source] && object.data["source"], + text: opts[:with_source] && get_source_text(object.data["source"]), created_at: created_at, edited_at: edited_at, reblogs_count: announcement_count, @@ -465,8 +465,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do %{ id: activity.id, - text: Map.get(object.data, "source", ""), - spoiler_text: Map.get(object.data, "summary", "") + text: get_source_text(Map.get(object.data, "source", "")), + spoiler_text: Map.get(object.data, "summary", ""), + content_type: get_source_content_type(object.data["source"]) } end @@ -687,4 +688,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end defp build_image_url(_, _), do: nil + + defp get_source_text(%{"content" => content} = _source) do + content + end + + defp get_source_text(source) when is_binary(source) do + source + end + + defp get_source_text(_) do + "" + end + + defp get_source_content_type(%{"mediaType" => type} = _source) do + type + end + + defp get_source_content_type(_source) do + Utils.get_content_type(nil) + end end -- cgit v1.2.3 From 06a3998013aca1f74c563d261d050543056c1255 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 5 Jun 2022 15:02:25 -0400 Subject: Create Update notifications --- lib/pleroma/notification.ex | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 52fd2656b..82aeb1802 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -385,7 +385,7 @@ defmodule Pleroma.Notification do end def create_notifications(%Activity{data: %{"type" => type}} = activity, options) - when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do + when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update"] do do_create_notifications(activity, options) end @@ -439,6 +439,9 @@ defmodule Pleroma.Notification do activity |> type_from_activity_object() + "Update" -> + "update" + t -> raise "No notification type for activity type #{t}" end @@ -513,7 +516,7 @@ defmodule Pleroma.Notification do def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) - when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do + when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag", "Update"] do potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receivers = @@ -553,6 +556,21 @@ defmodule Pleroma.Notification do (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor] end + # Update activity: notify all who repeated this + def get_potential_receiver_ap_ids(%{data: %{"type" => "Update", "actor" => actor}} = activity) do + with %Object{data: %{"id" => object_id}} <- Object.normalize(activity, fetch: false) do + repeaters = + Activity.Queries.by_type("Announce") + |> Activity.Queries.by_object_id(object_id) + |> Activity.with_joined_user_actor() + |> where([a, u], u.local) + |> select([a, u], u.ap_id) + |> Repo.all() + + repeaters -- [actor] + end + end + def get_potential_receiver_ap_ids(activity) do [] |> Utils.maybe_notify_to_recipients(activity) -- cgit v1.2.3 From 532f6ae3ede9b0795a164ca170314b95d5113fc8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 5 Jun 2022 16:34:42 -0400 Subject: Return update notification in mastodon api --- .../mastodon_api/controllers/notification_controller.ex | 1 + lib/pleroma/web/mastodon_api/views/notification_view.ex | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 932bc6423..e93930771 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -51,6 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do move pleroma:emoji_reaction poll + update } def index(%{assigns: %{user: user}} = conn, params) do params = diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 0dc7f3beb..b5b5b2376 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -19,7 +19,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - @parent_types ~w{Like Announce EmojiReact} + defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id + + defp object_id_for(%{data: %{"object" => id}}) when is_binary(id), do: id + + @parent_types ~w{Like Announce EmojiReact Update} def render("index.json", %{notifications: notifications, for: reading_user} = opts) do activities = Enum.map(notifications, & &1.activity) @@ -30,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do %{data: %{"type" => type}} -> type in @parent_types end) - |> Enum.map(& &1.data["object"]) + |> Enum.map(&object_id_for/1) |> Activity.create_by_object_ap_id() |> Activity.with_preloaded_object(:left) |> Pleroma.Repo.all() @@ -78,9 +82,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do parent_activity_fn = fn -> if opts[:parent_activities] do - Activity.Queries.find_by_object_ap_id(opts[:parent_activities], activity.data["object"]) + Activity.Queries.find_by_object_ap_id(opts[:parent_activities], object_id_for(activity)) else - Activity.get_create_by_object_ap_id(activity.data["object"]) + Activity.get_create_by_object_ap_id(object_id_for(activity)) end end @@ -109,6 +113,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do "reblog" -> put_status(response, parent_activity_fn.(), reading_user, status_render_opts) + "update" -> + put_status(response, parent_activity_fn.(), reading_user, status_render_opts) + "move" -> put_target(response, activity, reading_user, %{}) -- cgit v1.2.3 From d2d3532e5f3e5bcedc91fd0f5ac4ca69043348db Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 5 Jun 2022 16:35:01 -0400 Subject: Lint --- lib/pleroma/notification.ex | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 82aeb1802..2906c599d 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -516,7 +516,16 @@ defmodule Pleroma.Notification do def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) - when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag", "Update"] do + when type in [ + "Create", + "Like", + "Announce", + "Follow", + "Move", + "EmojiReact", + "Flag", + "Update" + ] do potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receivers = -- cgit v1.2.3 From 237b220d71bfe7db66db12549851fb93900a060a Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 8 Jun 2022 11:05:48 -0400 Subject: Add object id to uploaded attachments --- lib/pleroma/upload.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 242813dcd..7480c57a6 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -36,6 +36,7 @@ defmodule Pleroma.Upload do alias Ecto.UUID alias Pleroma.Config alias Pleroma.Maps + alias Pleroma.Web.ActivityPub.Utils require Logger @type source :: @@ -88,6 +89,7 @@ defmodule Pleroma.Upload do {:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do {:ok, %{ + "id" => Utils.generate_object_id(), "type" => opts.activity_type, "mediaType" => upload.content_type, "url" => [ -- cgit v1.2.3 From aafd7a687dea7595ee9431451d8e170fc3ff909e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 8 Jun 2022 11:45:24 -0400 Subject: Return the corresponding object id in attachment view --- lib/pleroma/web/common_api/utils.ex | 8 ++++++-- lib/pleroma/web/mastodon_api/views/status_view.ex | 13 +++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 4c6a26384..5fc8c3220 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do def attachments_from_ids_no_descs(ids) do Enum.map(ids, fn media_id -> - case Repo.get(Object, media_id) do + case get_attachment(media_id) do %Object{data: data} -> data _ -> nil end @@ -51,13 +51,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do {_, descs} = Jason.decode(descs_str) Enum.map(ids, fn media_id -> - with %Object{data: data} <- Repo.get(Object, media_id) do + with %Object{data: data} <- get_attachment(media_id) do Map.put(data, "name", descs[media_id]) end end) |> Enum.reject(&is_nil/1) end + defp get_attachment(media_id) do + Repo.get(Object, media_id) + end + @spec get_to_and_cc(ActivityDraft.t()) :: {list(String.t()), list(String.t())} def get_to_and_cc(%{in_reply_to_conversation: %Participation{} = participation}) do diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index f798b2624..43f5fa02e 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -523,10 +523,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do true -> "unknown" end - <> = :crypto.hash(:md5, href) + attachment_id = + with {_, ap_id} when is_binary(ap_id) <- {:ap_id, attachment["id"]}, + {_, %Object{data: _object_data, id: object_id}} <- + {:object, Object.get_by_ap_id(ap_id)} do + to_string(object_id) + else + _ -> + <> = :crypto.hash(:md5, href) + to_string(attachment["id"] || hash_id) + end %{ - id: to_string(attachment["id"] || hash_id), + id: attachment_id, url: href, remote_url: href, preview_url: href_preview, -- cgit v1.2.3 From c3593639adfdd6f9e086aaab18bda5c83bcfcc8b Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 9 Jun 2022 11:39:51 -0400 Subject: Fix incorrectly cached content after editing --- lib/pleroma/activity/html.ex | 36 ++++++++++++++++++++ lib/pleroma/application.ex | 1 + lib/pleroma/web/activity_pub/side_effects.ex | 27 +++++++++------ lib/pleroma/web/mastodon_api/views/status_view.ex | 41 ++++++++++++++++++++--- 4 files changed, 90 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity/html.ex b/lib/pleroma/activity/html.ex index 071a89c8d..706b2d36c 100644 --- a/lib/pleroma/activity/html.ex +++ b/lib/pleroma/activity/html.ex @@ -8,6 +8,40 @@ defmodule Pleroma.Activity.HTML do @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + # We store a list of cache keys related to an activity in a + # separate cache, scrubber_management_cache. It has the same + # size as scrubber_cache (see application.ex). Every time we add + # a cache to scrubber_cache, we update scrubber_management_cache. + # + # The most recent write of a certain key in the management cache + # is the same as the most recent write of any record related to that + # key in the main cache. + # Assuming LRW ( https://hexdocs.pm/cachex/Cachex.Policy.LRW.html ), + # this means when the management cache is evicted by cachex, all + # related records in the main cache will also have been evicted. + + defp get_cache_keys_for(activity_id) do + with {:ok, list} when is_list(list) <- @cachex.get(:scrubber_management_cache, activity_id) do + list + else + _ -> [] + end + end + + defp add_cache_key_for(activity_id, additional_key) do + current = get_cache_keys_for(activity_id) + + unless additional_key in current do + @cachex.put(:scrubber_management_cache, activity_id, [additional_key | current]) + end + end + + def invalidate_cache_for(activity_id) do + keys = get_cache_keys_for(activity_id) + Enum.map(keys, &@cachex.del(:scrubber_cache, &1)) + @cachex.del(:scrubber_management_cache, activity_id) + end + def get_cached_scrubbed_html_for_activity( content, scrubbers, @@ -19,6 +53,8 @@ defmodule Pleroma.Activity.HTML do @cachex.fetch!(:scrubber_cache, key, fn _key -> object = Object.normalize(activity, fetch: false) + + add_cache_key_for(activity.id, key) HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback) end) end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d808bc732..e6b733f9b 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -189,6 +189,7 @@ defmodule Pleroma.Application do build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000), build_cachex("scrubber", limit: 2500), + build_cachex("scrubber_management", limit: 2500), build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500), build_cachex("web_resp", limit: 2500), build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10), diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 05f9b9bd9..d387d9362 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -455,7 +455,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do %{data: %{"type" => "Update", "object" => updated_object}} = object, meta ) do - orig_object = Object.get_by_ap_id(updated_object["id"]) + orig_object_ap_id = updated_object["id"] + orig_object = Object.get_by_ap_id(orig_object_ap_id) orig_object_data = orig_object.data if orig_object_data["type"] in @updatable_object_types do @@ -468,15 +469,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do |> Object.maybe_update_history(orig_object_data, updated) |> maybe_update_poll(updated_object) - orig_object - |> Repo.preload(:hashtags) - |> Object.change(%{data: updated_object_data}) - |> Object.update_and_set_cache() - - if updated do - object - |> Activity.normalize() - |> ActivityPub.notify_and_stream() + changeset = + orig_object + |> Repo.preload(:hashtags) + |> Object.change(%{data: updated_object_data}) + + with {:ok, new_object} <- Repo.update(changeset), + {:ok, _} <- Object.invalid_object_cache(new_object), + {:ok, _} <- Object.set_cache(new_object), + # The metadata/utils.ex uses the object id for the cache. + {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do + if updated do + object + |> Activity.normalize() + |> ActivityPub.notify_and_stream() + end end end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 43f5fa02e..9cb2adcf9 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -272,6 +272,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"]) + history_len = + 1 + + (Object.history_for(object.data) + |> Map.get("orderedItems") + |> length()) + + # See render("history.json", ...) for more details + # Here the implicit index of the current content is 0 + chrono_order = history_len - 1 + content = object |> render_content() @@ -281,14 +291,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do |> Activity.HTML.get_cached_scrubbed_html_for_activity( User.html_filter_policy(opts[:for]), activity, - "mastoapi:content" + "mastoapi:content:#{chrono_order}" ) content_plaintext = content |> Activity.HTML.get_cached_stripped_html_for_activity( activity, - "mastoapi:content" + "mastoapi:content:#{chrono_order}" ) summary = object.data["summary"] || "" @@ -410,9 +420,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do history = [object | past_history] + history_len = length(history) + + history = + Enum.with_index( + history, + fn object, index -> + %{ + # The history is prepended every time there is a new edit. + # In chrono_order, the oldest item is always at 0, and so on. + # The chrono_order is an invariant kept between edits. + chrono_order: history_len - 1 - index, + object: object + } + end + ) + individual_opts = opts - |> Map.put(:as, :object) + |> Map.put(:as, :item) |> Map.put(:user, user) |> Map.put(:hashtags, hashtags) @@ -421,7 +447,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do def render( "history_item.json", - %{activity: activity, user: user, object: object, hashtags: hashtags} = opts + %{ + activity: activity, + user: user, + item: %{object: object, chrono_order: chrono_order}, + hashtags: hashtags + } = opts ) do sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw") @@ -439,7 +470,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do |> Activity.HTML.get_cached_scrubbed_html_for_activity( User.html_filter_policy(opts[:for]), activity, - "mastoapi:content" + "mastoapi:content:#{chrono_order}" ) summary = object.data["summary"] || "" -- cgit v1.2.3 From 27f3d802f2fd6e9d002654993d8eedb92d120055 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Jun 2022 10:35:36 -0400 Subject: Expose history and source apis to anon users --- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 10 ++++++---- lib/pleroma/web/router.ex | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index fa86e9dc0..e594ea491 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -195,8 +195,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/history" - def show_history(%{assigns: %{user: user}} = conn, %{id: id} = params) do - with %Activity{} = activity <- Activity.get_by_id_with_object(id), + def show_history(%{assigns: assigns} = conn, %{id: id} = params) do + with user = assigns[:user], + %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "history.json", activity: activity, @@ -210,8 +211,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/source" - def show_source(%{assigns: %{user: user}} = conn, %{id: id} = _params) do - with %Activity{} = activity <- Activity.get_by_id_with_object(id), + def show_source(%{assigns: assigns} = conn, %{id: id} = _params) do + with user = assigns[:user], + %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "source.json", activity: activity, diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 2d2e5365e..4a999f0c2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -552,8 +552,6 @@ defmodule Pleroma.Web.Router do get("/bookmarks", StatusController, :bookmarks) post("/statuses", StatusController, :create) - get("/statuses/:id/history", StatusController, :show_history) - get("/statuses/:id/source", StatusController, :show_source) put("/statuses/:id", StatusController, :update) delete("/statuses/:id", StatusController, :delete) post("/statuses/:id/reblog", StatusController, :reblog) @@ -611,6 +609,8 @@ defmodule Pleroma.Web.Router do get("/statuses/:id/card", StatusController, :card) get("/statuses/:id/favourited_by", StatusController, :favourited_by) get("/statuses/:id/reblogged_by", StatusController, :reblogged_by) + get("/statuses/:id/history", StatusController, :show_history) + get("/statuses/:id/source", StatusController, :show_source) get("/custom_emojis", CustomEmojiController, :index) -- cgit v1.2.3 From 7451f0e81f1fd378a3ff23d437e3cc6780d62fb4 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Jun 2022 12:02:16 -0400 Subject: Send the correct update in streamer get_create_by_ap_id_with_object() seems to fetch the old object. Why this happens needs further investigation. --- lib/pleroma/web/streamer.ex | 8 ++++++-- lib/pleroma/web/views/streamer_view.ex | 4 ---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 8b7fb985b..fe909df0a 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -297,12 +297,16 @@ defmodule Pleroma.Web.Streamer do defp push_to_socket(_topic, %Activity{data: %{"type" => "Delete"}}), do: :noop defp push_to_socket(topic, %Activity{data: %{"type" => "Update"}} = item) do - anon_render = StreamerView.render("status_update.json", item) + create_activity = + Pleroma.Activity.get_create_by_object_ap_id(item.object.data["id"]) + |> Map.put(:object, item.object) + + anon_render = StreamerView.render("status_update.json", create_activity) Registry.dispatch(@registry, topic, fn list -> Enum.each(list, fn {pid, auth?} -> if auth? do - send(pid, {:render_with_user, StreamerView, "status_update.json", item}) + send(pid, {:render_with_user, StreamerView, "status_update.json", create_activity}) else send(pid, {:text, anon_render}) end diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex index 797762d90..6a55242b0 100644 --- a/lib/pleroma/web/views/streamer_view.ex +++ b/lib/pleroma/web/views/streamer_view.ex @@ -26,8 +26,6 @@ defmodule Pleroma.Web.StreamerView do end def render("status_update.json", %Activity{} = activity, %User{} = user) do - activity = Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"]) - %{ event: "status.update", payload: @@ -68,8 +66,6 @@ defmodule Pleroma.Web.StreamerView do end def render("status_update.json", %Activity{} = activity) do - activity = Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"]) - %{ event: "status.update", payload: -- cgit v1.2.3 From 95b39223281a61f3ee7d52776df2713952de3be0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Jun 2022 16:28:59 -0400 Subject: Workaround with_index does not support function in Elixir 1.9 --- lib/pleroma/web/mastodon_api/views/status_view.ex | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 9cb2adcf9..6ede89803 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -423,18 +423,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do history_len = length(history) history = - Enum.with_index( - history, - fn object, index -> - %{ - # The history is prepended every time there is a new edit. - # In chrono_order, the oldest item is always at 0, and so on. - # The chrono_order is an invariant kept between edits. - chrono_order: history_len - 1 - index, - object: object - } - end - ) + Enum.zip(history_len..0, history) + |> Enum.map(fn {chrono_order, object} -> + %{ + # The history is prepended every time there is a new edit. + # In chrono_order, the oldest item is always at 0, and so on. + # The chrono_order is an invariant kept between edits. + chrono_order: chrono_order, + object: object + } + end) individual_opts = opts -- cgit v1.2.3 From 44613db853226015207977ee958ebbf4d26f7c00 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Jun 2022 19:52:07 -0400 Subject: Show original status at the first of history --- lib/pleroma/web/mastodon_api/views/status_view.ex | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 6ede89803..8439431eb 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -418,13 +418,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do |> Enum.map(&Map.put(&1, "id", object.data["id"])) |> Enum.map(&%Object{data: &1, id: object.id}) - history = [object | past_history] - - history_len = length(history) - history = - Enum.zip(history_len..0, history) - |> Enum.map(fn {chrono_order, object} -> + [object | past_history] + # Mastodon expects the original to be at the first + |> Enum.reverse() + |> Enum.with_index() + |> Enum.map(fn {object, chrono_order} -> %{ # The history is prepended every time there is a new edit. # In chrono_order, the oldest item is always at 0, and so on. -- cgit v1.2.3 From b0f83aea29be0b25075d19b35d5ff98ef8fbd000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 16 Jun 2022 18:12:30 +0200 Subject: Store mutes expiration date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/user.ex | 19 +++++++++-- lib/pleroma/user_relationship.ex | 39 +++++++++++++++++----- lib/pleroma/web/api_spec/schemas/account.ex | 1 + .../mastodon_api/controllers/account_controller.ex | 3 +- lib/pleroma/web/mastodon_api/views/account_view.ex | 11 ++++++ 5 files changed, 60 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 747a83e8d..88837b91a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1461,15 +1461,28 @@ defmodule Pleroma.User do notifications? = Map.get(params, :notifications, true) expires_in = Map.get(params, :expires_in, 0) - with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee), + expires_at = + if expires_in > 0 do + NaiveDateTime.utc_now() + |> NaiveDateTime.add(expires_in) + else + nil + end + + with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at), {:ok, user_notification_mute} <- - (notifications? && UserRelationship.create_notification_mute(muter, mutee)) || + (notifications? && + UserRelationship.create_notification_mute( + muter, + mutee, + expires_at + )) || {:ok, nil} do if expires_in > 0 do Pleroma.Workers.MuteExpireWorker.enqueue( "unmute_user", %{"muter_id" => muter.id, "mutee_id" => mutee.id}, - schedule_in: expires_in + scheduled_at: expires_at ) end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 1432a1d83..f35f3ac63 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -18,16 +18,17 @@ defmodule Pleroma.UserRelationship do belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType) field(:relationship_type, Pleroma.UserRelationship.Type) + field(:expires_at, :naive_datetime) timestamps(updated_at: false) end for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do - # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, - # `def create_notification_mute/2`, `def create_inverse_subscription/2`, - # `def endorsement/2` - def unquote(:"create_#{relationship_type}")(source, target), - do: create(unquote(relationship_type), source, target) + # `def create_block/3`, `def create_mute/3`, `def create_reblog_mute/3`, + # `def create_notification_mute/3`, `def create_inverse_subscription/3`, + # `def endorsement/3` + def unquote(:"create_#{relationship_type}")(source, target, expires_at \\ nil), + do: create(unquote(relationship_type), source, target, expires_at) # `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`, # `def delete_notification_mute/2`, `def delete_inverse_subscription/2`, @@ -37,9 +38,15 @@ defmodule Pleroma.UserRelationship do # `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`, # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`, - # `def inverse_endorsement?/2` + # `def inverse_endorsement_exists?/2` def unquote(:"#{relationship_type}_exists?")(source, target), do: exists?(unquote(relationship_type), source, target) + + # `def get_block_expire_date/2`, `def get_mute_expire_date/2`, + # `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`, + # `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2` + def unquote(:"get_#{relationship_type}_expire_date")(source, target), + do: get_expire_date(unquote(relationship_type), source, target) end def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) @@ -48,7 +55,7 @@ defmodule Pleroma.UserRelationship do def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do user_relationship - |> cast(params, [:relationship_type, :source_id, :target_id]) + |> cast(params, [:relationship_type, :source_id, :target_id, :expires_at]) |> validate_required([:relationship_type, :source_id, :target_id]) |> unique_constraint(:relationship_type, name: :user_relationships_source_id_relationship_type_target_id_index @@ -62,12 +69,26 @@ defmodule Pleroma.UserRelationship do |> Repo.exists?() end - def create(relationship_type, %User{} = source, %User{} = target) do + def get_expire_date(relationship_type, %User{} = source, %User{} = target) do + %UserRelationship{expires_at: expires_at} = + UserRelationship + |> where( + relationship_type: ^relationship_type, + source_id: ^source.id, + target_id: ^target.id + ) + |> Repo.one!() + + expires_at + end + + def create(relationship_type, %User{} = source, %User{} = target, expires_at \\ nil) do %UserRelationship{} |> changeset(%{ relationship_type: relationship_type, source_id: source.id, - target_id: target.id + target_id: target.id, + expires_at: expires_at }) |> Repo.insert( on_conflict: {:replace_all_except, [:id]}, diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index e8a529f2e..8aeb821a8 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -33,6 +33,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do header: %Schema{type: :string, format: :uri}, id: FlakeID, locked: %Schema{type: :boolean}, + mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true}, note: %Schema{type: :string, format: :html}, statuses_count: %Schema{type: :integer}, url: %Schema{type: :string, format: :uri}, diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 50c12a1b1..eb59a0a96 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -499,7 +499,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do users: users, for: user, as: :user, - embed_relationships: embed_relationships?(params) + embed_relationships: embed_relationships?(params), + mutes: true ) end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 988eedbb1..2260bf5da 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -311,6 +311,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> maybe_put_unread_conversation_count(user, opts[:for]) |> maybe_put_unread_notification_count(user, opts[:for]) |> maybe_put_email_address(user, opts[:for]) + |> maybe_put_mute_expires_at(user, opts[:for], opts) |> maybe_show_birthday(user, opts[:for]) end @@ -434,6 +435,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_email_address(data, _, _), do: data + defp maybe_put_mute_expires_at(data, %User{} = user, target, %{mutes: true}) do + Map.put( + data, + :mute_expires_at, + UserRelationship.get_mute_expire_date(target, user) + ) + end + + defp maybe_put_mute_expires_at(data, _, _, _), do: data + defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do data |> Kernel.put_in([:pleroma, :birthday], user.birthday) -- cgit v1.2.3 From 06da000c5d4fc8d71bd36bfb4cec9cbf4399dfe8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 21 Jun 2022 12:32:44 -0400 Subject: Add editing to features --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index ee52475d5..4f613416b 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -68,6 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do "shareable_emoji_packs", "multifetch", "pleroma:api/v1/notifications:include_types_filter", + "editing", if Config.get([:activitypub, :blockers_visible]) do "blockers_visible" end, -- cgit v1.2.3 From 01321c88b5ef0d6c236b968cc47eaa8873054b1e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 24 Jun 2022 10:10:22 -0400 Subject: Convert incoming Updated object into Pleroma format --- lib/pleroma/web/activity_pub/object_validator.ex | 16 ++++++++++++++++ .../object_validators/article_note_page_validator.ex | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index f3e31c931..b3105c46e 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -141,6 +141,22 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end + def validate( + %{"type" => "Update", "object" => %{"type" => objtype} = object} = update_activity, + meta + ) + when objtype in ~w[Question Answer Audio Video Event Article Note Page] do + with {:ok, object_data} <- cast_and_apply(object), + meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), + {:ok, update_activity} <- + update_activity + |> UpdateValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + update_activity = stringify_keys(update_activity) + {:ok, update_activity, meta} + end + end + def validate(%{"type" => type} = object, meta) when type in ~w[Accept Reject Follow Update Like EmojiReact Announce ChatMessage Answer] do diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index ca335bc8a..0a0d30e45 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -49,7 +49,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"]) defp fix_url(data), do: data - defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data + defp fix_tag(%{"tag" => tag} = data) when is_list(tag) do + Map.put(data, "tag", Enum.filter(tag, &is_map/1)) + end + defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag]) defp fix_tag(data), do: Map.drop(data, ["tag"]) -- cgit v1.2.3 From ee0738319169cf5c7d31dcaf641d9b744c7a72dc Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 24 Jun 2022 10:26:01 -0400 Subject: Use meta[:object_data] in SideEffects for Update --- lib/pleroma/web/activity_pub/side_effects.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index d387d9362..aa4183bf6 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -459,6 +459,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do orig_object = Object.get_by_ap_id(orig_object_ap_id) orig_object_data = orig_object.data + updated_object = meta[:object_data] + if orig_object_data["type"] in @updatable_object_types do %{data: updated_object_data, updated: updated} = orig_object_data -- cgit v1.2.3 From e0d6da4e7d52e2cdd0fc5290e4ff3a23da7398f6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 24 Jun 2022 10:54:11 -0400 Subject: Fix CommonAPITest --- lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex | 3 ++- lib/pleroma/web/activity_pub/object_validators/common_fields.ex | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index d1c61ac82..ca6e39612 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do @primary_key false embedded_schema do + field(:id, :string) field(:type, :string) field(:mediaType, :string, default: "application/octet-stream") field(:name, :string) @@ -43,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do |> fix_url() struct - |> cast(data, [:type, :mediaType, :name, :blurhash]) + |> cast(data, [:id, :type, :mediaType, :name, :blurhash]) |> cast_embed(:url, with: &url_changeset/2) |> validate_inclusion(:type, ~w[Link Document Audio Image Video]) |> validate_required([:type, :mediaType, :url]) diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 8e768ffbf..a59a6e545 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -33,6 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do field(:content, :string) field(:published, ObjectValidators.DateTime) + field(:updated, ObjectValidators.DateTime) field(:emoji, ObjectValidators.Emoji, default: %{}) embeds_many(:attachment, AttachmentValidator) end -- cgit v1.2.3 From 99a6f5031638da2eed237f91c6dded9e25717599 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 25 Jun 2022 00:32:22 -0400 Subject: Unify the logic of updating objects --- lib/pleroma/object.ex | 42 ------ lib/pleroma/object/fetcher.ex | 9 +- lib/pleroma/object/updater.ex | 157 ++++++++++++++++++++++ lib/pleroma/web/activity_pub/side_effects.ex | 62 +++------ lib/pleroma/web/common_api.ex | 10 +- lib/pleroma/web/mastodon_api/views/status_view.ex | 4 +- 6 files changed, 183 insertions(+), 101 deletions(-) create mode 100644 lib/pleroma/object/updater.ex (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 670ab8743..fe264b5e0 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -425,46 +425,4 @@ defmodule Pleroma.Object do end def object_data_hashtags(_), do: [] - - def history_for(object) do - with history <- Map.get(object, "formerRepresentations"), - true <- is_map(history), - "OrderedCollection" <- Map.get(history, "type"), - true <- is_list(Map.get(history, "orderedItems")), - true <- is_integer(Map.get(history, "totalItems")) do - history - else - _ -> history_skeleton() - end - end - - defp history_skeleton do - %{ - "type" => "OrderedCollection", - "totalItems" => 0, - "orderedItems" => [] - } - end - - def maybe_update_history(updated_object, orig_object_data, updated) do - if not updated do - updated_object - else - # Put edit history - # Note that we may have got the edit history by first fetching the object - history = Object.history_for(orig_object_data) - - latest_history_item = - orig_object_data - |> Map.drop(["id", "formerRepresentations"]) - - new_history = - history - |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) - |> Map.put("totalItems", history["totalItems"] + 1) - - updated_object - |> Map.put("formerRepresentations", new_history) - end - end end diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index ce816c1fc..d81fdcf24 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -50,7 +50,14 @@ defmodule Pleroma.Object.Fetcher do Pleroma.Constants.status_updatable_fields() |> Enum.any?(fn field -> Map.get(old_data, field) != Map.get(new_data, field) end) - new_data |> Object.maybe_update_history(old_data, changed?) + %{updated_object: updated_object} = + new_data + |> Object.Updater.maybe_update_history(old_data, + updated: changed?, + use_history_in_new_object?: false + ) + + updated_object else new_data end diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex new file mode 100644 index 000000000..03136c38e --- /dev/null +++ b/lib/pleroma/object/updater.ex @@ -0,0 +1,157 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Object.Updater do + require Pleroma.Constants + + def update_content_fields(orig_object_data, updated_object) do + Pleroma.Constants.status_updatable_fields() + |> Enum.reduce( + %{data: orig_object_data, updated: false}, + fn field, %{data: data, updated: updated} -> + updated = updated or Map.get(updated_object, field) != Map.get(orig_object_data, field) + + data = + if Map.has_key?(updated_object, field) do + Map.put(data, field, updated_object[field]) + else + Map.drop(data, [field]) + end + + %{data: data, updated: updated} + end + ) + end + + def maybe_history(object) do + with history <- Map.get(object, "formerRepresentations"), + true <- is_map(history), + "OrderedCollection" <- Map.get(history, "type"), + true <- is_list(Map.get(history, "orderedItems")), + true <- is_integer(Map.get(history, "totalItems")) do + history + else + _ -> nil + end + end + + def history_for(object) do + with history when not is_nil(history) <- maybe_history(object) do + history + else + _ -> history_skeleton() + end + end + + defp history_skeleton do + %{ + "type" => "OrderedCollection", + "totalItems" => 0, + "orderedItems" => [] + } + end + + def maybe_update_history( + updated_object, + orig_object_data, + opts + ) do + updated = opts[:updated] + use_history_in_new_object? = opts[:use_history_in_new_object?] + + if not updated do + %{updated_object: updated_object, used_history_in_new_object?: false} + else + # Put edit history + # Note that we may have got the edit history by first fetching the object + {new_history, used_history_in_new_object?} = + with true <- use_history_in_new_object?, + updated_history when not is_nil(updated_history) <- maybe_history(updated_object) do + {updated_history, true} + else + _ -> + history = history_for(orig_object_data) + + latest_history_item = + orig_object_data + |> Map.drop(["id", "formerRepresentations"]) + + updated_history = + history + |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) + |> Map.put("totalItems", history["totalItems"] + 1) + + {updated_history, false} + end + + updated_object = + updated_object + |> Map.put("formerRepresentations", new_history) + + %{updated_object: updated_object, used_history_in_new_object?: used_history_in_new_object?} + end + end + + defp maybe_update_poll(to_be_updated, updated_object) do + choice_key = fn data -> + if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf" + end + + with true <- to_be_updated["type"] == "Question", + key <- choice_key.(updated_object), + true <- key == choice_key.(to_be_updated), + orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])), + new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])), + true <- orig_choices == new_choices do + # Choices are the same, but counts are different + to_be_updated + |> Map.put(key, updated_object[key]) + else + # Choices (or vote type) have changed, do not allow this + _ -> to_be_updated + end + end + + # This calculates the data to be sent as the object of an Update. + # new_data's formerRepresentations is not considered. + # formerRepresentations is added to the returned data. + def make_update_object_data(original_data, new_data, date) do + %{data: updated_data, updated: updated} = + original_data + |> update_content_fields(new_data) + + if not updated do + updated_data + else + %{updated_object: updated_data} = + updated_data + |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false) + + updated_data + |> Map.put("updated", date) + end + end + + # This calculates the data of the new Object from an Update. + # new_data's formerRepresentations is considered. + def make_new_object_data_from_update_object(original_data, new_data) do + %{data: updated_data, updated: updated} = + original_data + |> update_content_fields(new_data) + + %{updated_object: updated_data, used_history_in_new_object?: used_history_in_new_object?} = + updated_data + |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false) + + updated_data = + updated_data + |> maybe_update_poll(new_data) + + %{ + updated_data: updated_data, + updated: updated, + used_history_in_new_object?: used_history_in_new_object? + } + end +end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index aa4183bf6..7345a6904 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -412,45 +412,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end @updatable_object_types ["Note", "Question"] - defp update_content_fields(orig_object_data, updated_object) do - Pleroma.Constants.status_updatable_fields() - |> Enum.reduce( - %{data: orig_object_data, updated: false}, - fn field, %{data: data, updated: updated} -> - updated = updated or Map.get(updated_object, field) != Map.get(orig_object_data, field) - - data = - if Map.has_key?(updated_object, field) do - Map.put(data, field, updated_object[field]) - else - Map.drop(data, [field]) - end - - %{data: data, updated: updated} - end - ) - end - - defp maybe_update_poll(to_be_updated, updated_object) do - choice_key = fn data -> - if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf" - end - - with true <- to_be_updated["type"] == "Question", - key <- choice_key.(updated_object), - true <- key == choice_key.(to_be_updated), - orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])), - new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])), - true <- orig_choices == new_choices do - # Choices are the same, but counts are different - to_be_updated - |> Map.put(key, updated_object[key]) - else - # Choices (or vote type) have changed, do not allow this - _ -> to_be_updated - end - end - defp handle_update_object( %{data: %{"type" => "Update", "object" => updated_object}} = object, meta @@ -462,14 +423,11 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do updated_object = meta[:object_data] if orig_object_data["type"] in @updatable_object_types do - %{data: updated_object_data, updated: updated} = - orig_object_data - |> update_content_fields(updated_object) - - updated_object_data = - updated_object_data - |> Object.maybe_update_history(orig_object_data, updated) - |> maybe_update_poll(updated_object) + %{ + updated_data: updated_object_data, + updated: updated, + used_history_in_new_object?: used_history_in_new_object? + } = Object.Updater.make_new_object_data_from_update_object(orig_object_data, updated_object) changeset = orig_object @@ -481,6 +439,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, _} <- Object.set_cache(new_object), # The metadata/utils.ex uses the object id for the cache. {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do + if used_history_in_new_object? do + with create_activity when not is_nil(create_activity) <- + Pleroma.Activity.get_create_by_object_ap_id(orig_object_ap_id), + {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(create_activity.id) do + nil + else + _ -> nil + end + end + if updated do object |> Activity.normalize() diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index e60c26053..e5a78c102 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -422,15 +422,7 @@ defmodule Pleroma.Web.CommonAPI do with {:ok, draft} <- ActivityDraft.create(user, params) do change = - Pleroma.Constants.status_updatable_fields() - |> Enum.reduce(orig_object.data, fn key, acc -> - if Map.has_key?(draft.object, key) do - acc |> Map.put(key, Map.get(draft.object, key)) - else - acc |> Map.drop([key]) - end - end) - |> Map.put("updated", Utils.make_date()) + Object.Updater.make_update_object_data(orig_object.data, draft.object, Utils.make_date()) {:ok, change} else diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 8439431eb..54e025aae 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -274,7 +274,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do history_len = 1 + - (Object.history_for(object.data) + (Object.Updater.history_for(object.data) |> Map.get("orderedItems") |> length()) @@ -413,7 +413,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do user = CommonAPI.get_user(activity.data["actor"]) past_history = - Object.history_for(object.data) + Object.Updater.history_for(object.data) |> Map.get("orderedItems") |> Enum.map(&Map.put(&1, "id", object.data["id"])) |> Enum.map(&%Object{data: &1, id: object.id}) -- cgit v1.2.3 From 40953a8f5c299e55b3f186bd6fdebe1bbf6e7401 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 25 Jun 2022 01:03:46 -0400 Subject: Reuse formerRepresentations from remote if possible --- lib/pleroma/object/updater.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex index 03136c38e..0b21f6c99 100644 --- a/lib/pleroma/object/updater.ex +++ b/lib/pleroma/object/updater.ex @@ -67,7 +67,7 @@ defmodule Pleroma.Object.Updater do # Note that we may have got the edit history by first fetching the object {new_history, used_history_in_new_object?} = with true <- use_history_in_new_object?, - updated_history when not is_nil(updated_history) <- maybe_history(updated_object) do + updated_history when not is_nil(updated_history) <- maybe_history(opts[:new_data]) do {updated_history, true} else _ -> @@ -142,7 +142,11 @@ defmodule Pleroma.Object.Updater do %{updated_object: updated_data, used_history_in_new_object?: used_history_in_new_object?} = updated_data - |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false) + |> maybe_update_history(original_data, + updated: updated, + use_history_in_new_object?: true, + new_data: new_data + ) updated_data = updated_data -- cgit v1.2.3 From 9c6dae942d2ec5e2314af1d345cf2aeed504aae8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 25 Jun 2022 09:23:09 -0400 Subject: Fix local updates causing emojis to be lost --- lib/pleroma/web/activity_pub/side_effects.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 7345a6904..747f467db 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -420,7 +420,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do orig_object = Object.get_by_ap_id(orig_object_ap_id) orig_object_data = orig_object.data - updated_object = meta[:object_data] + updated_object = + if meta[:local] do + # If this is a local Update, we don't process it by transmogrifier, + # so we use the embedded object as-is. + updated_object + else + meta[:object_data] + end if orig_object_data["type"] in @updatable_object_types do %{ -- cgit v1.2.3 From 5321fd001233076e7f2b6ea00adeb41ecf7e180a Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 25 Jun 2022 10:03:19 -0400 Subject: Do not put meta[:object_data] for local Updates --- lib/pleroma/web/activity_pub/object_validator.ex | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index b3105c46e..d4bf9c31e 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -146,7 +146,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do meta ) when objtype in ~w[Question Answer Audio Video Event Article Note Page] do - with {:ok, object_data} <- cast_and_apply(object), + with {_, false} <- {:local, Access.get(meta, :local, false)}, + {:ok, object_data} <- cast_and_apply(object), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), {:ok, update_activity} <- update_activity @@ -154,6 +155,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do |> Ecto.Changeset.apply_action(:insert) do update_activity = stringify_keys(update_activity) {:ok, update_activity, meta} + else + {:local, _} -> + with {:ok, object} <- + update_activity + |> UpdateValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end end end -- cgit v1.2.3 From 014096aeefe88348323db74e2ab7f81e0184bfee Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 25 Jun 2022 11:20:46 -0400 Subject: Make outbound transmogrifier aware of edit history --- lib/pleroma/constants.ex | 12 ++++++ lib/pleroma/web/activity_pub/side_effects.ex | 3 +- lib/pleroma/web/activity_pub/transmogrifier.ex | 52 +++++++++++++++++--------- 3 files changed, 47 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index bbb95104f..1b3d09d73 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -42,6 +42,18 @@ defmodule Pleroma.Constants do ] ) + const(updatable_object_types, + do: [ + "Note", + "Question", + "Audio", + "Video", + "Event", + "Article", + "Page" + ] + ) + const(actor_types, do: [ "Application", diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 747f467db..f56e357bf 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -411,7 +411,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end - @updatable_object_types ["Note", "Question"] defp handle_update_object( %{data: %{"type" => "Update", "object" => updated_object}} = object, meta @@ -429,7 +428,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do meta[:object_data] end - if orig_object_data["type"] in @updatable_object_types do + if orig_object_data["type"] in Pleroma.Constants.updatable_object_types() do %{ updated_data: updated_object_data, updated: updated, diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 5750396a4..cccee342f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -687,6 +687,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> strip_internal_fields |> strip_internal_tags |> set_type + |> maybe_process_history + end + + defp maybe_process_history(%{"formerRepresentations" => %{"orderedItems" => history}} = object) do + processed_history = + Enum.map( + history, + fn + item when is_map(item) -> prepare_object(item) + item -> item + end + ) + + put_in(object, ["formerRepresentations", "orderedItems"], processed_history) + end + + defp maybe_process_history(object) do + object end # @doc @@ -711,6 +729,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, data} end + def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data) + when objtype in Pleroma.Constants.updatable_object_types() do + object = + object + |> prepare_object + + data = + data + |> Map.put("object", object) + |> Map.merge(Utils.make_json_ld_header()) + |> Map.delete("bcc") + + {:ok, data} + end + def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do object = object_id @@ -902,24 +935,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def strip_internal_fields(object) do - outer = Map.drop(object, Pleroma.Constants.object_internal_fields()) - - case outer do - %{"formerRepresentations" => %{"orderedItems" => list}} when is_list(list) -> - update_in( - outer["formerRepresentations"]["orderedItems"], - &Enum.map( - &1, - fn - item when is_map(item) -> Map.drop(item, Pleroma.Constants.object_internal_fields()) - item -> item - end - ) - ) - - _ -> - outer - end + Map.drop(object, Pleroma.Constants.object_internal_fields()) end defp strip_internal_tags(%{"tag" => tags} = object) do -- cgit v1.2.3 From 11f9f2ef277937d5558a1cc0a92a60b872f17de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 28 Jun 2022 20:33:50 +0200 Subject: EmojiReactValidator: fix emoji qualification Tries fully-qualifying emoji when receiving them, by adding the emoji variation sequence to the received reaction emoji. This issue arises when other instance software, such as Misskey, tries reacting with emoji that have unqualified or minimally qualified variants, like a red heart. Pleroma only accepts fully qualified emoji in emoji reactions, and refused those emoji. Now, Pleroma will attempt to properly qualify them first, and reject them if checks still fail. --- .../object_validators/emoji_react_validator.ex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index ed072b888..bf5166633 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -49,6 +49,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp fix(data) do data = data + |> fix_emoji_qualification() |> CommonFixes.fix_actor() |> CommonFixes.fix_activity_addressing() @@ -61,6 +62,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do end end + defp fix_emoji_qualification(%{"content" => emoji} = data) do + # Emoji variation sequence + new_emoji = emoji <> "\uFE0F" + + if Pleroma.Emoji.is_unicode_emoji?(new_emoji) do + data + |> Map.put("content", new_emoji) + else + data + end + end + + defp fix_emoji_qualification(data), do: data + defp validate_emoji(cng) do content = get_field(cng, :content) -- cgit v1.2.3 From 4367489a3e8eb8682d717014eea9092d7679c070 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 3 Jul 2022 20:02:52 -0400 Subject: Pass history items through ObjectValidator for updatable object types --- lib/pleroma/web/activity_pub/object_validator.ex | 73 +++++++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index d4bf9c31e..12278a46b 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -128,15 +128,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end with {:ok, object} <- - object - |> validator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - - # Insert copy of hashtags as strings for the non-hashtag table indexing - tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object}) - object = Map.put(object, "tag", tag) - + do_separate_with_history(object, fn object -> + with {:ok, object} <- + object + |> validator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + + # Insert copy of hashtags as strings for the non-hashtag table indexing + tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object}) + object = Map.put(object, "tag", tag) + + {:ok, object} + end + end) do {:ok, object, meta} end end @@ -262,4 +267,54 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do Object.normalize(object["object"], fetch: true) :ok end + + defp for_each_history_item( + %{"type" => "OrderedCollection", "orderedItems" => items} = history, + object, + fun + ) do + processed_items = + Enum.map(items, fn item -> + with item <- Map.put(item, "id", object["id"]), + {:ok, item} <- fun.(item) do + item + else + _ -> nil + end + end) + + if Enum.all?(processed_items, &(not is_nil(&1))) do + {:ok, Map.put(history, "orderedItems", processed_items)} + else + {:error, :invalid_history} + end + end + + defp for_each_history_item(nil, _object, _fun) do + {:ok, nil} + end + + defp for_each_history_item(_, _object, _fun) do + {:error, :invalid_history} + end + + # fun is (object -> {:ok, validated_object_with_string_keys}) + defp do_separate_with_history(object, fun) do + with history <- object["formerRepresentations"], + object <- Map.drop(object, ["formerRepresentations"]), + {_, {:ok, object}} <- {:main_body, fun.(object)}, + {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do + object = + if history do + Map.put(object, "formerRepresentations", history) + else + object + end + + {:ok, object} + else + {:main_body, e} -> e + {:history_items, e} -> e + end + end end -- cgit v1.2.3 From 5ce118d970d3d7a2a5dd0a3719feb1d53be6b5ae Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 3 Jul 2022 20:19:50 -0400 Subject: Validate object data for incoming Update activities In Create validator we do not validate the object data, but that is because the object itself will go through the pipeline again, which is not the case for Update. Thus, we added validation for objects in Update activities. --- lib/pleroma/web/activity_pub/object_validator.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 12278a46b..3ccb4a3d6 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -152,8 +152,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do ) when objtype in ~w[Question Answer Audio Video Event Article Note Page] do with {_, false} <- {:local, Access.get(meta, :local, false)}, - {:ok, object_data} <- cast_and_apply(object), - meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), + {_, {:ok, object_data, _}} <- {:object_validation, validate(object, meta)}, + meta = Keyword.put(meta, :object_data, object_data), {:ok, update_activity} <- update_activity |> UpdateValidator.cast_and_validate() @@ -169,6 +169,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do object = stringify_keys(object) {:ok, object, meta} end + + {:object_validation, e} -> + e end end -- cgit v1.2.3 From f84ed44cea1e5793dd899c74c38336a1721889e6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 6 Jul 2022 01:19:53 -0400 Subject: Fix cannot get full history on object fetch --- lib/pleroma/web/activity_pub/object_validator.ex | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 3ccb4a3d6..9f446100d 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -103,8 +103,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do meta ) when objtype in ~w[Question Answer Audio Video Event Article Note Page] do - with {:ok, object_data} <- cast_and_apply(object), - meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), + with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object), + meta = Keyword.put(meta, :object_data, object_data), {:ok, create_activity} <- create_activity |> CreateGenericValidator.cast_and_validate(meta) @@ -212,6 +212,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def validate(o, m), do: {:error, {:validator_not_set, {o, m}}} + def cast_and_apply_and_stringify_with_history(object) do + do_separate_with_history(object, fn object -> + with {:ok, object_data} <- cast_and_apply(object), + object_data <- object_data |> stringify_keys() do + {:ok, object_data} + end + end) + end + def cast_and_apply(%{"type" => "ChatMessage"} = object) do ChatMessageValidator.cast_and_apply(object) end -- cgit v1.2.3 From 069554e9253a47f99225e12cc0ee99700fb89c6e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 7 Jul 2022 15:11:29 -0400 Subject: Guard against outdated Updates It is possible for an earlier Update to be received by us later. For this, we now (1) only allows Updates to poll counts if there is no updated field, or the updated field is the same as the last updated date or creation date; (2) does not allow updating anything if the updated field is older than the last updated date or creation date; (3) allows updating updatable fields otherwise (normal updates); (4) if only the updated field is changed, it does not create a new history item on its own. --- lib/pleroma/object/updater.ex | 65 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex index 0b21f6c99..3d34c3f27 100644 --- a/lib/pleroma/object/updater.ex +++ b/lib/pleroma/object/updater.ex @@ -10,7 +10,10 @@ defmodule Pleroma.Object.Updater do |> Enum.reduce( %{data: orig_object_data, updated: false}, fn field, %{data: data, updated: updated} -> - updated = updated or Map.get(updated_object, field) != Map.get(orig_object_data, field) + updated = + updated or + (field != "updated" and + Map.get(updated_object, field) != Map.get(orig_object_data, field)) data = if Map.has_key?(updated_object, field) do @@ -136,21 +139,57 @@ defmodule Pleroma.Object.Updater do # This calculates the data of the new Object from an Update. # new_data's formerRepresentations is considered. def make_new_object_data_from_update_object(original_data, new_data) do - %{data: updated_data, updated: updated} = - original_data - |> update_content_fields(new_data) + update_is_reasonable = + with {_, updated} when not is_nil(updated) <- {:cur_updated, new_data["updated"]}, + {_, {:ok, updated_time, _}} <- {:cur_updated, DateTime.from_iso8601(updated)}, + {_, last_updated} when not is_nil(last_updated) <- + {:last_updated, original_data["updated"] || original_data["published"]}, + {_, {:ok, last_updated_time, _}} <- + {:last_updated, DateTime.from_iso8601(last_updated)}, + :gt <- DateTime.compare(updated_time, last_updated_time) do + :update_everything + else + # only allow poll updates + {:cur_updated, _} -> :no_content_update + :eq -> :no_content_update + # allow all updates + {:last_updated, _} -> :update_everything + # allow no updates + _ -> false + end - %{updated_object: updated_data, used_history_in_new_object?: used_history_in_new_object?} = - updated_data - |> maybe_update_history(original_data, - updated: updated, - use_history_in_new_object?: true, - new_data: new_data - ) + %{ + updated_object: updated_data, + used_history_in_new_object?: used_history_in_new_object?, + updated: updated + } = + if update_is_reasonable == :update_everything do + %{data: updated_data, updated: updated} = + original_data + |> update_content_fields(new_data) + + updated_data + |> maybe_update_history(original_data, + updated: updated, + use_history_in_new_object?: true, + new_data: new_data + ) + |> Map.put(:updated, updated) + else + %{ + updated_object: original_data, + used_history_in_new_object?: false, + updated: false + } + end updated_data = - updated_data - |> maybe_update_poll(new_data) + if update_is_reasonable != false do + updated_data + |> maybe_update_poll(new_data) + else + updated_data + end %{ updated_data: updated_data, -- cgit v1.2.3 From 04ded94a50fbabb194ab9e9c5cf8f08937f85d64 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 9 Jul 2022 18:00:42 -0400 Subject: Fix remote emoji in subject disappearing after edits --- lib/pleroma/web/common_api.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index e5a78c102..89f5dd606 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -415,7 +415,14 @@ defmodule Pleroma.Web.CommonAPI do defp make_update_data(user, orig_object, changes) do kept_params = %{ - visibility: Visibility.get_visibility(orig_object) + visibility: Visibility.get_visibility(orig_object), + in_reply_to_id: + with replied_id when is_binary(replied_id) <- orig_object.data["inReplyTo"], + %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(replied_id) do + activity_id + else + _ -> nil + end } params = Map.merge(changes, kept_params) -- cgit v1.2.3 From c1874bc8f943599383fe0a03f129d3113c1cf301 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 12 Jul 2022 19:03:18 -0400 Subject: Make mutes and blocks behave the same as other lists --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 50c12a1b1..83d0f718d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -491,7 +491,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do users = user |> User.muted_users_relation(_restrict_deactivated = true) - |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + |> Pleroma.Pagination.fetch_paginated(params) conn |> add_link_headers(users) @@ -508,7 +508,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do users = user |> User.blocked_users_relation(_restrict_deactivated = true) - |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + |> Pleroma.Pagination.fetch_paginated(params) conn |> add_link_headers(users) -- cgit v1.2.3 From b2a0718e83c2d61fc081ca3a1b3cd67a47da1e98 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 13 Jul 2022 17:59:23 -0400 Subject: Extract config descriptions for translation --- lib/pleroma/docs/translator.ex | 9 ++++ lib/pleroma/docs/translator/compiler.ex | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 lib/pleroma/docs/translator.ex create mode 100644 lib/pleroma/docs/translator/compiler.ex (limited to 'lib') diff --git a/lib/pleroma/docs/translator.ex b/lib/pleroma/docs/translator.ex new file mode 100644 index 000000000..db985b7c2 --- /dev/null +++ b/lib/pleroma/docs/translator.ex @@ -0,0 +1,9 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Docs.Translator do + require Pleroma.Web.Gettext + + @before_compile Pleroma.Docs.Translator.Compiler +end diff --git a/lib/pleroma/docs/translator/compiler.ex b/lib/pleroma/docs/translator/compiler.ex new file mode 100644 index 000000000..21c02b50b --- /dev/null +++ b/lib/pleroma/docs/translator/compiler.ex @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Docs.Translator.Compiler do + @external_resource "config/description.exs" + @raw_config Pleroma.Config.Loader.read("config/description.exs") + @raw_descriptions @raw_config[:pleroma][:config_description] + + defmacro __before_compile__(_env) do + strings = + __MODULE__.descriptions() + |> __MODULE__.extract_strings() + + quote do + def placeholder do + unquote do + Enum.map( + strings, + fn string -> + quote do + Pleroma.Web.Gettext.dgettext_noop("config_descriptions", unquote(string)) + end + end + ) + end + end + end + end + + def descriptions do + Pleroma.Web.ActivityPub.MRF.config_descriptions() + |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end) + |> Pleroma.Docs.Generator.convert_to_strings() + end + + def extract_strings(descriptions) do + descriptions + |> Enum.reduce([], &process_item/2) + end + + defp process_item(entity, acc) do + current_level = + acc + |> process_desc(entity) + |> process_label(entity) + + process_children(entity, current_level) + end + + defp process_desc(acc, %{description: desc}) do + [desc | acc] + end + + defp process_desc(acc, _) do + acc + end + + defp process_label(acc, %{label: label}) do + [label | acc] + end + + defp process_label(acc, _) do + acc + end + + defp process_children(%{children: children}, acc) do + children + |> Enum.reduce(acc, &process_item/2) + end + + defp process_children(_, acc) do + acc + end +end -- cgit v1.2.3 From 20588517fc5ed86c77865d879027618a1eb54106 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 13 Jul 2022 18:31:35 -0400 Subject: Make admin api use translated config descriptions --- .../web/admin_api/controllers/config_controller.ex | 34 +++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index 55ab6d063..f6fbbc910 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -22,10 +22,42 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation + defp translate_descriptions(descriptions) do + Enum.map(descriptions, &translate_item/1) + end + + defp translate_string(str) do + Gettext.dgettext(Pleroma.Web.Gettext, "config_descriptions", str) + end + + defp maybe_put_translated(item, key) do + if item[key] do + Map.put(item, key, translate_string(item[key])) + else + item + end + end + + defp translate_item(item) do + item + |> maybe_put_translated(:label) + |> maybe_put_translated(:description) + |> translate_children() + end + + defp translate_children(%{children: children} = item) when is_list(children) do + item + |> Map.put(:children, translate_descriptions(children)) + end + + defp translate_children(item) do + item + end + def descriptions(conn, _params) do descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1) - json(conn, descriptions) + json(conn, translate_descriptions(descriptions)) end def show(conn, %{only_db: true}) do -- cgit v1.2.3 From 7473868880b21850eaa5431b2db66af05a854405 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 13 Jul 2022 18:46:21 -0400 Subject: Fix compile error --- lib/pleroma/docs/translator.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/docs/translator.ex b/lib/pleroma/docs/translator.ex index db985b7c2..13e33c87e 100644 --- a/lib/pleroma/docs/translator.ex +++ b/lib/pleroma/docs/translator.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.Translator do + require Pleroma.Docs.Translator.Compiler require Pleroma.Web.Gettext @before_compile Pleroma.Docs.Translator.Compiler -- cgit v1.2.3 From 28626eafc174e6707ab4020f72a5550446730da9 Mon Sep 17 00:00:00 2001 From: floatingghost Date: Thu, 14 Jul 2022 13:35:33 +0200 Subject: Allow higher amount of restarts for Pleroma.Repo during testing This was done by floatingghost as part of a bigger commit in Akkoma. See . As explained in > there are so many caches that clearing them all can nuke the supervisor, which by default will become an hero if it gets more than 3 restarts in <5 seconds And further down the thread > essentially we've got like 11 caches (https://akkoma.dev/AkkomaGang/akkoma/src/commit/37ae047e1652c4089934434ec79f393c4c839122/lib/pleroma/application.ex#L165) > then in test we fetch them all (https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/test/support/data_case.ex#L50) and call clear on them > so if this clear fails on any 3 of them, the pleroma supervisor itself will die How it fails? > idk maybe cachex dies, maybe :ets does a weird thing > it doesn't log anything, it just consistently dies during cache clearing so i figured it had to be that > honestly my best bet is locksmith and queuing > https://github.com/whitfin/cachex/blob/master/lib/cachex/actions/clear.ex#L26 > clear is thrown into a locksmith transaction > locksmith says > >If the process is already in a transactional context, the provided function will be executed immediately. Otherwise the required keys will be locked until the provided function has finished executing. > so if we get 2 clears too close together, maybe it locks, then doesn't like the next clear? --- lib/pleroma/application.ex | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d808bc732..ae3ef9738 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -112,7 +112,17 @@ defmodule Pleroma.Application do # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options - opts = [strategy: :one_for_one, name: Pleroma.Supervisor] + # If we have a lot of caches, default max_restarts can cause test + # resets to fail. + # Go for the default 3 unless we're in test + max_restarts = + if @mix_env == :test do + 100 + else + 3 + end + + opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts] result = Supervisor.start_link(children, opts) set_postgres_server_version() -- cgit v1.2.3 From 1d7e8d6e013bb39e6ca61bd595a22490412db084 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 14 Jul 2022 17:41:33 -0400 Subject: Pass in msgctxt for config translation strings --- lib/pleroma/docs/translator/compiler.ex | 62 ++++++++++++++++++---- .../web/admin_api/controllers/config_controller.ex | 42 ++++++++++----- 2 files changed, 82 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/docs/translator/compiler.ex b/lib/pleroma/docs/translator/compiler.ex index 21c02b50b..5d27d9fa2 100644 --- a/lib/pleroma/docs/translator/compiler.ex +++ b/lib/pleroma/docs/translator/compiler.ex @@ -17,9 +17,15 @@ defmodule Pleroma.Docs.Translator.Compiler do unquote do Enum.map( strings, - fn string -> + fn {path, type, string} -> + ctxt = msgctxt_for(path, type) + quote do - Pleroma.Web.Gettext.dgettext_noop("config_descriptions", unquote(string)) + Pleroma.Web.Gettext.dpgettext_noop( + "config_descriptions", + unquote(ctxt), + unquote(string) + ) end end ) @@ -36,7 +42,8 @@ defmodule Pleroma.Docs.Translator.Compiler do def extract_strings(descriptions) do descriptions - |> Enum.reduce([], &process_item/2) + |> Enum.reduce(%{strings: [], path: []}, &process_item/2) + |> Map.get(:strings) end defp process_item(entity, acc) do @@ -48,28 +55,65 @@ defmodule Pleroma.Docs.Translator.Compiler do process_children(entity, current_level) end - defp process_desc(acc, %{description: desc}) do - [desc | acc] + defp process_desc(acc, %{description: desc} = item) do + %{ + strings: [{acc.path ++ [key_for(item)], "description", desc} | acc.strings], + path: acc.path + } end defp process_desc(acc, _) do acc end - defp process_label(acc, %{label: label}) do - [label | acc] + defp process_label(acc, %{label: label} = item) do + %{ + strings: [{acc.path ++ [key_for(item)], "label", label} | acc.strings], + path: acc.path + } end defp process_label(acc, _) do acc end - defp process_children(%{children: children}, acc) do + defp process_children(%{children: children} = item, acc) do + current_level = Map.put(acc, :path, acc.path ++ [key_for(item)]) + children - |> Enum.reduce(acc, &process_item/2) + |> Enum.reduce(current_level, &process_item/2) + |> Map.put(:path, acc.path) end defp process_children(_, acc) do acc end + + def msgctxt_for(path, type) do + "config #{type} at #{Enum.join(path, " > ")}" + end + + defp convert_group({_, group}) do + group + end + + defp convert_group(group) do + group + end + + def key_for(%{group: group, key: key}) do + "#{convert_group(group)}-#{key}" + end + + def key_for(%{group: group}) do + convert_group(group) + end + + def key_for(%{key: key}) do + key + end + + def key_for(_) do + nil + end end diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index f6fbbc910..a03318c0e 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -22,35 +22,51 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation - defp translate_descriptions(descriptions) do - Enum.map(descriptions, &translate_item/1) + defp translate_descriptions(descriptions, path \\ []) do + Enum.map(descriptions, fn desc -> translate_item(desc, path) end) end - defp translate_string(str) do - Gettext.dgettext(Pleroma.Web.Gettext, "config_descriptions", str) + defp translate_string(str, path, type) do + Gettext.dpgettext( + Pleroma.Web.Gettext, + "config_descriptions", + Pleroma.Docs.Translator.Compiler.msgctxt_for(path, type), + str + ) end - defp maybe_put_translated(item, key) do + defp maybe_put_translated(item, key, path) do if item[key] do - Map.put(item, key, translate_string(item[key])) + Map.put( + item, + key, + translate_string( + item[key], + path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)], + to_string(key) + ) + ) else item end end - defp translate_item(item) do + defp translate_item(item, path) do item - |> maybe_put_translated(:label) - |> maybe_put_translated(:description) - |> translate_children() + |> maybe_put_translated(:label, path) + |> maybe_put_translated(:description, path) + |> translate_children(path) end - defp translate_children(%{children: children} = item) when is_list(children) do + defp translate_children(%{children: children} = item, path) when is_list(children) do item - |> Map.put(:children, translate_descriptions(children)) + |> Map.put( + :children, + translate_descriptions(children, path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)]) + ) end - defp translate_children(item) do + defp translate_children(item, _path) do item end -- cgit v1.2.3 From 8371fd8ca20d7aaa16e082fd7ed39d603b9731d1 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 16 Jul 2022 01:20:25 -0400 Subject: Implement settings api --- .../operations/pleroma_settings_operation.ex | 72 ++++++++++++++++++++ .../pleroma_api/controllers/settings_controller.ex | 79 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 7 ++ 3 files changed, 158 insertions(+) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex create mode 100644 lib/pleroma/web/pleroma_api/controllers/settings_controller.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex new file mode 100644 index 000000000..e2cef4f67 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaSettingsOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def show_operation do + %Operation{ + tags: ["Settings"], + summary: "Get settings for an application", + description: "Get synchronized settings for an application", + operationId: "SettingsController.show", + parameters: [app_name_param()], + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => Operation.response("object", "application/json", object()) + } + } + end + + def update_operation do + %Operation{ + tags: ["Settings"], + summary: "Update settings for an application", + description: "Update synchronized settings for an application", + operationId: "SettingsController.update", + parameters: [app_name_param()], + security: [%{"oAuth" => ["write:accounts"]}], + requestBody: request_body("Parameters", update_request(), required: true), + responses: %{ + 200 => Operation.response("object", "application/json", object()) + } + } + end + + def app_name_param do + Operation.parameter(:app, :path, %Schema{type: :string}, "Application name", + example: "pleroma-fe", + required: true + ) + end + + def object do + %Schema{ + title: "Settings object", + description: "The object that contains settings for the application.", + type: :object + } + end + + def update_request do + %Schema{ + title: "SettingsUpdateRequest", + type: :object, + description: + "The settings object to be merged with the current settings. To remove a field, set it to null.", + example: %{ + "config1" => true, + "config2_to_unset" => nil + } + } + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex new file mode 100644 index 000000000..1136575b6 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex @@ -0,0 +1,79 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.SettingsController do + use Pleroma.Web, :controller + + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} when action in [:update] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} when action in [:show] + ) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaSettingsOperation + + @doc "GET /api/v1/pleroma/settings/:app" + def show(%{assigns: %{user: user}} = conn, %{app: app} = _params) do + conn + |> json(get_settings(user, app)) + end + + @doc "PATCH /api/v1/pleroma/settings/:app" + def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{app: app} = _params) do + settings = + get_settings(user, app) + |> merge_recursively(body_params) + + with changeset <- + Pleroma.User.update_changeset( + user, + %{pleroma_settings_store: %{app => settings}} + ), + {:ok, _} <- Pleroma.Repo.update(changeset) do + conn + |> json(settings) + end + end + + defp merge_recursively(old, %{} = new) do + old = ensure_object(old) + + Enum.reduce( + new, + old, + fn + {k, nil}, acc -> + Map.drop(acc, [k]) + + {k, %{} = new_child}, acc -> + Map.put(acc, k, merge_recursively(acc[k], new_child)) + + {k, v}, acc -> + Map.put(acc, k, v) + end + ) + end + + defp get_settings(user, app) do + user.pleroma_settings_store + |> Map.get(app, %{}) + |> ensure_object() + end + + defp ensure_object(%{} = object) do + object + end + + defp ensure_object(_) do + %{} + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 7bbc20275..9023b9800 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -458,6 +458,13 @@ defmodule Pleroma.Web.Router do get("/birthdays", AccountController, :birthdays) end + scope [] do + pipe_through(:authenticated_api) + + get("/settings/:app", SettingsController, :show) + patch("/settings/:app", SettingsController, :update) + end + post("/accounts/confirmation_resend", AccountController, :confirmation_resend) end -- cgit v1.2.3 From 597f56b4c4db3c63ff66fc93717d3b20655e1543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 16 Jul 2022 16:09:47 +0200 Subject: Use :utc_datetime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/user.ex | 4 ++-- lib/pleroma/user_relationship.ex | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 88837b91a..44754879a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1463,8 +1463,8 @@ defmodule Pleroma.User do expires_at = if expires_in > 0 do - NaiveDateTime.utc_now() - |> NaiveDateTime.add(expires_in) + DateTime.utc_now() + |> DateTime.add(expires_in) else nil end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index f35f3ac63..5b3e593d3 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -18,7 +18,7 @@ defmodule Pleroma.UserRelationship do belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType) field(:relationship_type, Pleroma.UserRelationship.Type) - field(:expires_at, :naive_datetime) + field(:expires_at, :utc_datetime) timestamps(updated_at: false) end -- cgit v1.2.3 From 64e16e6a4be83e9328d62101cc21d10cf07e38fb Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 16 Jul 2022 23:44:37 -0600 Subject: Document way to do notice compatibility routes with Nginx reverse-proxy instead --- lib/pleroma/web/router.ex | 5 ----- lib/pleroma/web/static_fe/static_fe_controller.ex | 9 --------- 2 files changed, 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 7bbc20275..ac3de8af3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -678,11 +678,6 @@ defmodule Pleroma.Web.Router do get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) - # Notice compatibility routes for other frontends - get("/@:nickname/:id", OStatus.OStatusController, :notice) - get("/@:nickname/posts/:id", OStatus.OStatusController, :notice) - get("/:nickname/status/:id", OStatus.OStatusController, :notice) - # Mastodon compatibility routes get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object) get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity) diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index b20a3689c..97c41c6f9 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -167,15 +167,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), do: assign(conn, :notice_id, notice_id) - defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts), - do: assign(conn, :notice_id, notice_id) - - defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts), - do: assign(conn, :notice_id, notice_id) - - defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts), - do: assign(conn, :notice_id, notice_id) - defp assign_id(%{path_info: ["users", user_id]} = conn, _opts), do: assign(conn, :username_or_id, user_id) -- cgit v1.2.3 From fb268c43786d1b058a6ec1b53939d4c122ac2af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 17 Jul 2022 19:46:29 +0200 Subject: Allow to unset birthday MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../web/api_spec/operations/account_operation.ex | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index a64762285..4111d1613 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -545,10 +545,18 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Invite token required when the registrations aren't public" }, birthday: %Schema{ - type: :string, nullable: true, description: "User's birthday", - format: :date + anyOf: [ + %Schema{ + type: :string, + format: :date + }, + %Schema{ + type: :string, + maxLength: 0 + } + ] }, language: %Schema{ type: :string, @@ -733,10 +741,18 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do }, actor_type: ActorType, birthday: %Schema{ - type: :string, nullable: true, description: "User's birthday", - format: :date + anyOf: [ + %Schema{ + type: :string, + format: :date + }, + %Schema{ + type: :string, + maxLength: 0 + } + ] }, show_birthday: %Schema{ allOf: [BooleanLike], -- cgit v1.2.3 From be98900904c05b601e7bffe29a662906d291fb67 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 22 Jul 2022 20:02:53 +0200 Subject: ArticleNotePageValidator: Fix when attachments are a Map (ie. owncast) --- .../activity_pub/object_validators/article_note_page_validator.ex | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index ca335bc8a..57c8d1dc0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -65,6 +65,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp fix_replies(data), do: data + def fix_attachments(%{"attachment" => attachment} = data) when is_map(attachment), + do: Map.put(data, "attachment", [attachment]) + + def fix_attachments(data), do: data + defp fix(data) do data |> CommonFixes.fix_actor() @@ -72,6 +77,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> fix_url() |> fix_tag() |> fix_replies() + |> fix_attachments() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() end -- cgit v1.2.3 From eba16665752d3e6be6b5a2df8cebeb4e22c9cad0 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 22 Jul 2022 20:22:44 +0200 Subject: AttachmentValidator: fix_media_type/1 fallback to application/octet-stream --- lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index 8b641d88d..cf6e407e0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -59,7 +59,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do end def fix_media_type(data) do - Map.put_new(data, "mediaType", data["mimeType"]) + Map.put_new(data, "mediaType", data["mimeType"] || "application/octet-stream") end defp handle_href(href, mediaType, data) do -- cgit v1.2.3 From cfb21d011fbb8a06ffcd63840e4db3aae3643045 Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 22 Jul 2022 23:19:49 +0000 Subject: Revert "Merge branch 'fix/emoji-react-qualification' into 'develop'" This reverts merge request !3684 --- .../object_validators/emoji_react_validator.ex | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index bf5166633..ed072b888 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -49,7 +49,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp fix(data) do data = data - |> fix_emoji_qualification() |> CommonFixes.fix_actor() |> CommonFixes.fix_activity_addressing() @@ -62,20 +61,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do end end - defp fix_emoji_qualification(%{"content" => emoji} = data) do - # Emoji variation sequence - new_emoji = emoji <> "\uFE0F" - - if Pleroma.Emoji.is_unicode_emoji?(new_emoji) do - data - |> Map.put("content", new_emoji) - else - data - end - end - - defp fix_emoji_qualification(data), do: data - defp validate_emoji(cng) do content = get_field(cng, :content) -- cgit v1.2.3 From eba9b0760f294482823b9bd55a430979fc2d21af Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 15:56:36 -0400 Subject: Make MRF Keyword history-aware --- lib/pleroma/object/updater.ex | 40 +++++++++++++++ lib/pleroma/web/activity_pub/mrf/keyword_policy.ex | 57 +++++++++++++++++----- 2 files changed, 84 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex index 3d34c3f27..6381320bd 100644 --- a/lib/pleroma/object/updater.ex +++ b/lib/pleroma/object/updater.ex @@ -197,4 +197,44 @@ defmodule Pleroma.Object.Updater do used_history_in_new_object?: used_history_in_new_object? } end + + defp for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do + new_items = + Enum.map(items, fun) + |> Enum.reduce_while( + {:ok, []}, + fn + {:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}} + e, _acc -> {:halt, e} + end + ) + + case new_items do + {:ok, items} -> {:ok, Map.put(history, "orderedItems", items)} + e -> e + end + end + + defp for_each_history_item(history, _, _) do + {:ok, history} + end + + def do_with_history(object, fun) do + with history <- object["formerRepresentations"], + object <- Map.drop(object, ["formerRepresentations"]), + {_, {:ok, object}} <- {:main_body, fun.(object)}, + {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do + object = + if history do + Map.put(object, "formerRepresentations", history) + else + object + end + + {:ok, object} + else + {:main_body, e} -> e + {:history_items, e} -> e + end + end end diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 00b64744f..687ec6c2f 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -27,24 +27,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end defp check_reject(%{"object" => %{} = object} = message) do - payload = object_payload(object) - - if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern -> - string_matches?(payload, pattern) - end) do - {:reject, "[KeywordPolicy] Matches with rejected keyword"} - else + with {:ok, _new_object} <- + Pleroma.Object.Updater.do_with_history(object, fn object -> + payload = object_payload(object) + + if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern -> + string_matches?(payload, pattern) + end) do + {:reject, "[KeywordPolicy] Matches with rejected keyword"} + else + {:ok, message} + end + end) do {:ok, message} + else + e -> e end end - defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do - payload = object_payload(object) + defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do + check_keyword = fn object -> + payload = object_payload(object) - if Pleroma.Constants.as_public() in to and - Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern -> + if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern -> string_matches?(payload, pattern) end) do + {:should_delist, nil} + else + {:ok, %{}} + end + end + + should_delist? = fn object -> + with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do + false + else + _ -> true + end + end + + if Pleroma.Constants.as_public() in to and should_delist?.(object) do to = List.delete(to, Pleroma.Constants.as_public()) cc = [Pleroma.Constants.as_public() | message["cc"] || []] @@ -59,8 +81,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end end + defp check_ftl_removal(message) do + {:ok, message} + end + defp check_replace(%{"object" => %{} = object} = message) do - object = + replace_kw = fn object -> ["content", "name", "summary"] |> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end) |> Enum.reduce(object, fn field, object -> @@ -73,6 +99,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do Map.put(object, field, data) end) + |> (fn object -> {:ok, object} end).() + end + + {:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw) message = Map.put(message, "object", object) @@ -80,7 +110,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end @impl true - def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do + def filter(%{"type" => type, "object" => %{"content" => _content}} = message) + when type in ["Create", "Update"] do with {:ok, message} <- check_reject(message), {:ok, message} <- check_ftl_removal(message), {:ok, message} <- check_replace(message) do -- cgit v1.2.3 From cd19537f391b792ee67c728320801d5a247ceb2c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 17:48:39 -0400 Subject: Make EnsureRePrepended history-aware --- lib/pleroma/object/updater.ex | 4 +- lib/pleroma/web/activity_pub/mrf.ex | 45 +++++++++++++++++++++- .../web/activity_pub/mrf/ensure_re_prepended.ex | 6 ++- lib/pleroma/web/activity_pub/mrf/policy.ex | 3 +- 4 files changed, 52 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex index 6381320bd..ab38d3ed2 100644 --- a/lib/pleroma/object/updater.ex +++ b/lib/pleroma/object/updater.ex @@ -198,7 +198,7 @@ defmodule Pleroma.Object.Updater do } end - defp for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do + def for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do new_items = Enum.map(items, fun) |> Enum.reduce_while( @@ -215,7 +215,7 @@ defmodule Pleroma.Object.Updater do end end - defp for_each_history_item(history, _, _) do + def for_each_history_item(history, _, _) do {:ok, history} end diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 323ecdbf1..ff9f84497 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -53,10 +53,53 @@ defmodule Pleroma.Web.ActivityPub.MRF do @required_description_keys [:key, :related_policy] + def filter_one(policy, message) do + should_plug_history? = + if function_exported?(policy, :history_awareness, 0) do + policy.history_awareness() + else + :manual + end + |> Kernel.==(:auto) + + if not should_plug_history? do + policy.filter(message) + else + main_result = policy.filter(message) + + with {_, {:ok, main_message}} <- {:main, main_result}, + {_, + %{ + "formerRepresentations" => %{ + "orderedItems" => [_ | _] + } + }} = {_, object} <- {:object, message["object"]}, + {_, {:ok, new_history}} <- + {:history, + Pleroma.Object.Updater.for_each_history_item( + object["formerRepresentations"], + object, + fn item -> + with {:ok, filtered} <- policy.filter(Map.put(message, "object", item)) do + {:ok, filtered["object"]} + else + e -> e + end + end + )} do + {:ok, put_in(main_message, ["object", "formerRepresentations"], new_history)} + else + {:main, _} -> main_result + {:object, _} -> main_result + {:history, e} -> e + end + end + end + def filter(policies, %{} = message) do policies |> Enum.reduce({:ok, message}, fn - policy, {:ok, message} -> policy.filter(message) + policy, {:ok, message} -> filter_one(policy, message) _, error -> error end) end diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 51596c09f..a148cc1e7 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) + def history_awareness, do: :auto + def filter_by_summary( %{data: %{"summary" => parent_summary}} = _in_reply_to, %{"summary" => child_summary} = child @@ -27,8 +29,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do def filter_by_summary(_in_reply_to, child), do: child - def filter(%{"type" => "Create", "object" => child_object} = object) - when is_map(child_object) do + def filter(%{"type" => type, "object" => child_object} = object) + when type in ["Create", "Update"] and is_map(child_object) do child = child_object["inReplyTo"] |> Object.normalize(fetch: false) diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex index 0ac250c3d..0234de4d5 100644 --- a/lib/pleroma/web/activity_pub/mrf/policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/policy.ex @@ -12,5 +12,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do label: String.t(), description: String.t() } - @optional_callbacks config_description: 0 + @callback history_awareness() :: :auto | :manual + @optional_callbacks config_description: 0, history_awareness: 0 end -- cgit v1.2.3 From 0a337063e14a63b3ed80776b493e3c9c56dd95d1 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 22:23:57 -0400 Subject: Make ForceMentionsInContent history-aware --- lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex index 255910b2f..70224561c 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex @@ -11,6 +11,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do @behaviour Pleroma.Web.ActivityPub.MRF.Policy + @impl true + def history_awareness, do: :auto + defp do_extract({:a, attrs, _}, acc) do if Enum.find(attrs, fn {name, value} -> name == "class" && value in ["mention", "u-url mention", "mention u-url"] @@ -74,11 +77,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do @impl true def filter( %{ - "type" => "Create", + "type" => type, "object" => %{"type" => "Note", "to" => to, "inReplyTo" => in_reply_to} } = object ) - when is_list(to) and is_binary(in_reply_to) do + when type in ["Create", "Update"] and is_list(to) and is_binary(in_reply_to) do # image-only posts from pleroma apparently reach this MRF without the content field content = object["object"]["content"] || "" -- cgit v1.2.3 From dce7e429286dfe8cb44a27c50713a03f0e696357 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 22:34:03 -0400 Subject: Make MediaProxyWarmingPolicy history-aware --- lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index 0eac8f021..c95d35bb9 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -16,6 +16,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do recv_timeout: 10_000 ] + @impl true + def history_awareness, do: :auto + defp prefetch(url) do # Fetching only proxiable resources if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do @@ -54,10 +57,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do end @impl true - def filter( - %{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message - ) - when is_list(attachments) and length(attachments) > 0 do + def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = message) + when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do preload(message) {:ok, message} -- cgit v1.2.3 From fc7ce5f93c4031863cbaf62b72dce55b5b6b0390 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 22:41:04 -0400 Subject: Make NoPlaceholderTextPolicy history-aware --- lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex index aab647d8e..f81e9e52a 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex @@ -6,14 +6,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do @moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + @impl true + def history_awareness, do: :auto + @impl true def filter( %{ - "type" => "Create", + "type" => type, "object" => %{"content" => content, "attachment" => _} = _child_object } = object ) - when content in [".", "

.

"] do + when type in ["Create", "Update"] and content in [".", "

.

"] do {:ok, put_in(object, ["object", "content"], "")} end -- cgit v1.2.3 From 46a5c06853c21e720b41a4b38a4d88a38a218ad4 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 22:50:38 -0400 Subject: Make NormalizeMarkup history-aware --- lib/pleroma/web/activity_pub/mrf/normalize_markup.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index dc2c19d49..2dfc9a901 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -9,7 +9,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do @behaviour Pleroma.Web.ActivityPub.MRF.Policy @impl true - def filter(%{"type" => "Create", "object" => child_object} = object) do + def history_awareness, do: :auto + + @impl true + def filter(%{"type" => type, "object" => child_object} = object) + when type in ["Create", "Update"] do scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy]) content = -- cgit v1.2.3 From 82c8fc1ede26837d024ecc2fd1231c6d2a3c2c3e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 23 Jul 2022 23:24:25 -0400 Subject: Make NoEmptyPolicy work with Update --- lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex index 4dc96e068..855cda3b9 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do @impl true def filter(%{"actor" => actor} = object) do with true <- is_local?(actor), + true <- is_eligible_type?(object), true <- is_note?(object), false <- has_attachment?(object), true <- only_mentions?(object) do @@ -32,7 +33,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do end defp has_attachment?(%{ - "type" => "Create", "object" => %{"type" => "Note", "attachment" => attachments} }) when length(attachments) > 0, @@ -40,7 +40,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do defp has_attachment?(_), do: false - defp only_mentions?(%{"type" => "Create", "object" => %{"type" => "Note", "source" => source}}) do + defp only_mentions?(%{"object" => %{"type" => "Note", "source" => source}}) do + source = + case source do + %{"content" => text} -> text + _ -> source + end + non_mentions = source |> String.split() |> Enum.filter(&(not String.starts_with?(&1, "@"))) |> length @@ -53,9 +59,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do defp only_mentions?(_), do: false - defp is_note?(%{"type" => "Create", "object" => %{"type" => "Note"}}), do: true + defp is_note?(%{"object" => %{"type" => "Note"}}), do: true defp is_note?(_), do: false + defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true + defp is_eligible_type?(_), do: false + @impl true def describe, do: {:ok, %{}} end -- cgit v1.2.3 From d877d2a4e7449e942b4d192f283824eebcade563 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 24 Jul 2022 00:02:39 -0400 Subject: Make HashtagPolicy history-aware --- lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex | 47 +++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex index 2142b7add..b73fd974c 100644 --- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex @@ -16,6 +16,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy + @impl true + def history_awareness, do: :manual + defp check_reject(message, hashtags) do if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do {:reject, "[HashtagPolicy] Matches with rejected keyword"} @@ -47,22 +50,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do defp check_ftl_removal(message, _hashtags), do: {:ok, message} - defp check_sensitive(message, hashtags) do - if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do - {:ok, Kernel.put_in(message, ["object", "sensitive"], true)} - else - {:ok, message} - end + defp check_sensitive(message) do + {:ok, new_object} = + Object.Updater.do_with_history(message["object"], fn object -> + hashtags = Object.hashtags(%Object{data: object}) + + if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do + {:ok, Map.put(object, "sensitive", true)} + else + {:ok, object} + end + end) + + {:ok, Map.put(message, "object", new_object)} end @impl true - def filter(%{"type" => "Create", "object" => object} = message) do - hashtags = Object.hashtags(%Object{data: object}) + def filter(%{"type" => type, "object" => object} = message) when type in ["Create", "Update"] do + history_items = + with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do + items + else + _ -> [] + end + + historical_hashtags = + Enum.reduce(history_items, [], fn item, acc -> + acc ++ Object.hashtags(%Object{data: item}) + end) + + hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags if hashtags != [] do with {:ok, message} <- check_reject(message, hashtags), - {:ok, message} <- check_ftl_removal(message, hashtags), - {:ok, message} <- check_sensitive(message, hashtags) do + {:ok, message} <- + (if "type" == "Create" do + check_ftl_removal(message, hashtags) + else + {:ok, message} + end), + {:ok, message} <- check_sensitive(message) do {:ok, message} end else -- cgit v1.2.3 From 997f08b3500a983e8b27db9a6e4745582bb4763c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 24 Jul 2022 00:18:09 -0400 Subject: Make AntiLinkSpamPolicy history-aware --- lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index f0504ead4..3ec9c52ee 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -9,6 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do require Logger + @impl true + def history_awareness, do: :auto + # has the user successfully posted before? defp old_user?(%User{} = u) do u.note_count > 0 || u.follower_count > 0 -- cgit v1.2.3 From 388bbc4978a97eb82554c6d8ebc8bb6c9d429080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sun, 24 Jul 2022 13:31:35 +0200 Subject: EmojiReactValidator: fix emoji qualification Tries fully-qualifying emoji when receiving them, by adding the emoji variation sequence to the received reaction emoji. This issue arises when other instance software, such as Misskey, tries reacting with emoji that have unqualified or minimally qualified variants, like a red heart. Pleroma only accepts fully qualified emoji in emoji reactions, and refused those emoji. Now, Pleroma will attempt to properly qualify them first, and reject them if checks still fail. This commit contains changes to tests proposed by lanodan. Co-authored-by: Haelwenn --- .../object_validators/emoji_react_validator.ex | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index ed072b888..2eb4f6842 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -49,6 +49,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp fix(data) do data = data + |> fix_emoji_qualification() |> CommonFixes.fix_actor() |> CommonFixes.fix_activity_addressing() @@ -61,6 +62,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do end end + defp fix_emoji_qualification(%{"content" => emoji} = data) do + # Emoji variation sequence + new_emoji = emoji <> "\uFE0F" + + cond do + Pleroma.Emoji.is_unicode_emoji?(emoji) -> + data + + Pleroma.Emoji.is_unicode_emoji?(new_emoji) -> + data |> Map.put("content", new_emoji) + + true -> + data + end + end + + defp fix_emoji_qualification(data), do: data + defp validate_emoji(cng) do content = get_field(cng, :content) -- cgit v1.2.3 From 01d396585e428ea1ca7e21868d7303a0bd8ffd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Mon, 25 Jul 2022 16:20:12 +0200 Subject: Emoji: implement full-qualifier using combinations This implements fully_qualify_emoji/1, which will return the fully-qualified version of an emoji if it knows of one, or return the emoji unmodified if not. This code generates combinations per emoji: for each FE0F, all possible combinations of the character being removed or staying will be generated. This is made as an attempt to find all partially-qualified and unqualified versions of a fully-qualified emoji. I have found *no cases* for which this would be a problem, after browsing the entire emoji list in emoji-test.txt. This is safe, and, sadly, most likely the sanest too. --- lib/pleroma/emoji.ex | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index 35f0da816..3726ef185 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -137,4 +137,49 @@ defmodule Pleroma.Emoji do end def is_unicode_emoji?(_), do: false + + # FE0F is the emoji variation sequence. It is used for fully-qualifying + # emoji, and that includes emoji combinations. + # This code generates combinations per emoji: for each FE0F, all possible + # combinations of the character being removed or staying will be generated. + # This is made as an attempt to find all partially-qualified and unqualified + # versions of a fully-qualified emoji. + # I have found *no cases* for which this would be a problem, after browsing + # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most + # likely sane too. + emoji_qualification_map = + emojis + |> Enum.filter(&String.contains?(&1, "\uFE0F")) + |> Enum.map(fn emoji -> + combinate = fn x, combinate -> + case x do + [] -> + [[]] + + ["\uFE0F" | tail] -> + combinate.(tail, combinate) + |> Enum.flat_map(fn x -> [x, ["\uFE0F" | x]] end) + + [codepoint | tail] -> + combinate.(tail, combinate) + |> Enum.map(fn x -> [codepoint | x] end) + end + end + + unqualified_list = + emoji + |> String.codepoints() + |> combinate.(combinate) + |> Enum.map(&List.to_string/1) + + {emoji, unqualified_list} + end) + + for {qualified, unqualified_list} <- emoji_qualification_map do + for unqualified <- unqualified_list do + def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified) + end + end + + def fully_qualify_emoji(emoji), do: emoji end -- cgit v1.2.3 From fb3f6e1975fc44414af66377061bf30ceee9f9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Mon, 25 Jul 2022 16:49:23 +0200 Subject: EmojiReactValidator: use new qualification method --- .../web/activity_pub/object_validators/emoji_react_validator.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index 2eb4f6842..0858281e5 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -63,8 +63,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do end defp fix_emoji_qualification(%{"content" => emoji} = data) do - # Emoji variation sequence - new_emoji = emoji <> "\uFE0F" + new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji) cond do Pleroma.Emoji.is_unicode_emoji?(emoji) -> -- cgit v1.2.3 From b99f5d61834ffd86f9e8aeca2b00c704f0a0467e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 26 Jul 2022 01:38:59 +0200 Subject: Emoji: split qualification variation into a module --- lib/pleroma/emoji.ex | 35 ++------------------------------- lib/pleroma/emoji/combinations.ex | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 33 deletions(-) create mode 100644 lib/pleroma/emoji/combinations.ex (limited to 'lib') diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index 3726ef185..dd65d56ae 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Emoji do """ use GenServer + alias Pleroma.Emoji.Combinations alias Pleroma.Emoji.Loader require Logger @@ -138,42 +139,10 @@ defmodule Pleroma.Emoji do def is_unicode_emoji?(_), do: false - # FE0F is the emoji variation sequence. It is used for fully-qualifying - # emoji, and that includes emoji combinations. - # This code generates combinations per emoji: for each FE0F, all possible - # combinations of the character being removed or staying will be generated. - # This is made as an attempt to find all partially-qualified and unqualified - # versions of a fully-qualified emoji. - # I have found *no cases* for which this would be a problem, after browsing - # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most - # likely sane too. emoji_qualification_map = emojis |> Enum.filter(&String.contains?(&1, "\uFE0F")) - |> Enum.map(fn emoji -> - combinate = fn x, combinate -> - case x do - [] -> - [[]] - - ["\uFE0F" | tail] -> - combinate.(tail, combinate) - |> Enum.flat_map(fn x -> [x, ["\uFE0F" | x]] end) - - [codepoint | tail] -> - combinate.(tail, combinate) - |> Enum.map(fn x -> [codepoint | x] end) - end - end - - unqualified_list = - emoji - |> String.codepoints() - |> combinate.(combinate) - |> Enum.map(&List.to_string/1) - - {emoji, unqualified_list} - end) + |> Combinations.variate_emoji_qualification() for {qualified, unqualified_list} <- emoji_qualification_map do for unqualified <- unqualified_list do diff --git a/lib/pleroma/emoji/combinations.ex b/lib/pleroma/emoji/combinations.ex new file mode 100644 index 000000000..c49466406 --- /dev/null +++ b/lib/pleroma/emoji/combinations.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.Combinations do + # FE0F is the emoji variation sequence. It is used for fully-qualifying + # emoji, and that includes emoji combinations. + # This code generates combinations per emoji: for each FE0F, all possible + # combinations of the character being removed or staying will be generated. + # This is made as an attempt to find all partially-qualified and unqualified + # versions of a fully-qualified emoji. + # I have found *no cases* for which this would be a problem, after browsing + # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most + # likely sane too. + + defp qualification_combinations([]), do: [[]] + + defp qualification_combinations(["\uFE0F" | tail]) do + tail + |> qualification_combinations() + |> Enum.flat_map(fn x -> [x, ["\uFE0F" | x]] end) + end + + defp qualification_combinations([codepoint | tail]) do + tail + |> qualification_combinations() + |> Enum.map(fn x -> [codepoint | x] end) + end + + def variate_emoji_qualification(emoji) when is_binary(emoji) do + emoji + |> String.codepoints() + |> qualification_combinations() + |> Enum.map(&List.to_string/1) + end + + def variate_emoji_qualification(emoji) when is_list(emoji) do + emoji + |> Enum.map(fn emoji -> {emoji, variate_emoji_qualification(emoji)} end) + end +end -- cgit v1.2.3 From 7167de592e3523459a1eb65d902085e828f962b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 26 Jul 2022 23:15:09 +0200 Subject: Emoji: apply recommended tail call changes Behavior matches previous code. Co-authored-by: Tusooa Zhu --- lib/pleroma/emoji/combinations.ex | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/combinations.ex b/lib/pleroma/emoji/combinations.ex index c49466406..981c73596 100644 --- a/lib/pleroma/emoji/combinations.ex +++ b/lib/pleroma/emoji/combinations.ex @@ -13,18 +13,22 @@ defmodule Pleroma.Emoji.Combinations do # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most # likely sane too. - defp qualification_combinations([]), do: [[]] + defp qualification_combinations(codepoints) do + qualification_combinations([[]], codepoints) + end - defp qualification_combinations(["\uFE0F" | tail]) do - tail - |> qualification_combinations() - |> Enum.flat_map(fn x -> [x, ["\uFE0F" | x]] end) + defp qualification_combinations(acc, []), do: acc + + defp qualification_combinations(acc, ["\uFE0F" | tail]) do + acc + |> Enum.flat_map(fn x -> [x, x ++ ["\uFE0F"]] end) + |> qualification_combinations(tail) end - defp qualification_combinations([codepoint | tail]) do - tail - |> qualification_combinations() - |> Enum.map(fn x -> [codepoint | x] end) + defp qualification_combinations(acc, [codepoint | tail]) do + acc + |> Enum.map(&Kernel.++(&1, [codepoint])) + |> qualification_combinations(tail) end def variate_emoji_qualification(emoji) when is_binary(emoji) do -- cgit v1.2.3 From 5d3d6a58f72888b8714605032b417091a8891bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 31 Jul 2022 17:22:34 +0200 Subject: Use `duration` param for mute expiration duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/user.ex | 8 ++++---- lib/pleroma/web/api_spec/operations/account_operation.ex | 15 +++++++++++++-- .../web/mastodon_api/controllers/account_controller.ex | 4 ++++ 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 18699f0c8..870e8c457 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1480,12 +1480,12 @@ defmodule Pleroma.User do {:ok, list(UserRelationship.t())} | {:error, String.t()} def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do notifications? = Map.get(params, :notifications, true) - expires_in = Map.get(params, :expires_in, 0) + duration = Map.get(params, :duration, 0) expires_at = - if expires_in > 0 do + if duration > 0 do DateTime.utc_now() - |> DateTime.add(expires_in) + |> DateTime.add(duration) else nil end @@ -1499,7 +1499,7 @@ defmodule Pleroma.User do expires_at )) || {:ok, nil} do - if expires_in > 0 do + if duration > 0 do Pleroma.Workers.MuteExpireWorker.enqueue( "unmute_user", %{"muter_id" => muter.id, "mutee_id" => mutee.id}, diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 4111d1613..97616f5e7 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -278,11 +278,17 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do %Schema{allOf: [BooleanLike], default: true}, "Mute notifications in addition to statuses? Defaults to `true`." ), + Operation.parameter( + :duration, + :query, + %Schema{type: :integer}, + "Expire the mute in `duration` seconds. Default 0 for infinity" + ), Operation.parameter( :expires_in, :query, %Schema{type: :integer, default: 0}, - "Expire the mute in `expires_in` seconds. Default 0 for infinity" + "Deprecated, use `duration` instead" ) ], responses: %{ @@ -877,10 +883,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Mute notifications in addition to statuses? Defaults to true.", default: true }, + duration: %Schema{ + type: :integer, + nullable: true, + description: "Expire the mute in `expires_in` seconds. Default 0 for infinity" + }, expires_in: %Schema{ type: :integer, nullable: true, - description: "Expire the mute in `expires_in` seconds. Default 0 for infinity", + description: "Deprecated, use `duration` instead", default: 0 } }, diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 2aeb339f0..bf931dc6b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -411,6 +411,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/mute" def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do + params = + params + |> Map.put_new(:duration, Map.get(params, :expires_in, 0)) + with {:ok, _user_relationships} <- User.mute(muter, muted, params) do render(conn, "relationship.json", user: muter, target: muted) else -- cgit v1.2.3 From a4fa286d200b4f0c0ac9f453eb3e0a0526560a20 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 2 Aug 2022 10:15:56 -0400 Subject: Use actor_types() to determine whether the Update is for user --- lib/pleroma/web/activity_pub/side_effects.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index f56e357bf..5eefd2824 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -163,8 +163,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do updated_object_id = updated_object["id"] with {_, true} <- {:has_id, is_binary(updated_object_id)}, - {_, user} <- {:user, Pleroma.User.get_by_ap_id(updated_object_id)} do - if user do + %{"type" => type} <- updated_object, + {_, is_user} <- {:is_user, type in Pleroma.Constants.actor_types()} do + if is_user do handle_update_user(object, meta) else handle_update_object(object, meta) -- cgit v1.2.3 From f2a9285ff089fbae043091898fb016f4aa16f689 Mon Sep 17 00:00:00 2001 From: floatingghost Date: Sat, 23 Jul 2022 18:58:45 +0000 Subject: bugfix/follow-state (#104) Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/104 --- lib/mix/tasks/pleroma/user.ex | 32 ++++++++++++++++++++++++++++++++ lib/pleroma/user.ex | 8 +++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 96d4eb90b..50ffb7f27 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -421,6 +421,38 @@ defmodule Mix.Tasks.Pleroma.User do |> Stream.run() end + def run(["fix_follow_state", local_user, remote_user]) do + start_pleroma() + + with {:local, %User{} = local} <- {:local, User.get_by_nickname(local_user)}, + {:remote, %User{} = remote} <- {:remote, User.get_by_nickname(remote_user)}, + {:follow_data, %{data: %{"state" => request_state}}} <- + {:follow_data, Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(local, remote)} do + calculated_state = User.following?(local, remote) + + shell_info( + "Request state is #{request_state}, vs calculated state of following=#{calculated_state}" + ) + + if calculated_state == false && request_state == "accept" do + shell_info("Discrepancy found, fixing") + Pleroma.Web.CommonAPI.reject_follow_request(local, remote) + shell_info("Relationship fixed") + else + shell_info("No discrepancy found") + end + else + {:local, _} -> + shell_error("No local user #{local_user}") + + {:remote, _} -> + shell_error("No remote user #{remote_user}") + + {:follow_data, _} -> + shell_error("No follow data for #{local_user} and #{remote_user}") + end + end + defp set_moderator(user, value) do {:ok, user} = user diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index eeea240fb..a57295891 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1574,13 +1574,19 @@ defmodule Pleroma.User do blocker end - # clear any requested follows as well + # clear any requested follows from both sides as well blocked = case CommonAPI.reject_follow_request(blocked, blocker) do {:ok, %User{} = updated_blocked} -> updated_blocked nil -> blocked end + blocker = + case CommonAPI.reject_follow_request(blocker, blocked) do + {:ok, %User{} = updated_blocker} -> updated_blocker + nil -> blocker + end + unsubscribe(blocked, blocker) unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true) -- cgit v1.2.3 From a0166e92fac596651ecaad78659a0f6907ccb6bd Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 6 Aug 2022 00:31:36 -0400 Subject: Treat MRF rejects as success in Oban worker --- lib/pleroma/workers/receiver_worker.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 268b5f30f..309e197dc 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -9,6 +9,11 @@ defmodule Pleroma.Workers.ReceiverWorker do @impl Oban.Worker def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do - Federator.perform(:incoming_ap_doc, params) + with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do + {:ok, res} + else + {:error, {:reject, reason}} -> {:cancel, reason} + e -> e + end end end -- cgit v1.2.3 From d487e0160cdc4cdf84c45e4c64f6589b317479cc Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 8 Aug 2022 08:41:33 -0400 Subject: Treat containment failure as cancel in ReceiverWorker --- lib/pleroma/workers/receiver_worker.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 309e197dc..c41b44e14 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Workers.ReceiverWorker do with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do {:ok, res} else + {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} {:error, {:reject, reason}} -> {:cancel, reason} e -> e end -- cgit v1.2.3 From a7f01ffc1d0795f65b34b6dd9337d665f27edff9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 00:34:04 -0400 Subject: Make backups require its own scope --- lib/pleroma/web/pleroma_api/controllers/backup_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex index 1a0548295..b9daed22b 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 -- cgit v1.2.3 From f3e061c9645571997a01b1091d0e8a3f68c6bb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sat, 6 Aug 2022 03:24:31 +0200 Subject: Object: remove context_id field 30 to 70% of the objects in the object table are simple JSON objects containing a single field, 'id', being the context's ID. The reason for the creation of an object per context seems to be an old relic from the StatusNet era, and has only been used nowadays as an helper for threads in Pleroma-FE via the `pleroma.conversation_id` field in status views. An object per context was created, and its numerical ID (table column) was used and stored as 'context_id' in the object and activity along with the full 'context' URI/string. This commit removes this field and stops creation of objects for each context, which will also allow incoming activities to use activity IDs as contexts, something which was not possible before, or would have been very broken under most circumstances. The `pleroma.conversation_id` field has been reimplemented in a way to maintain backwards-compatibility by calculating a CRC32 of the full context URI/string in the object, instead of relying on the row ID for the created context object. --- lib/pleroma/object.ex | 4 --- .../article_note_page_validator.ex | 2 +- .../activity_pub/object_validators/common_fixes.ex | 4 +-- .../object_validators/event_validator.ex | 2 +- .../object_validators/question_validator.ex | 2 +- lib/pleroma/web/activity_pub/utils.ex | 23 ++--------------- lib/pleroma/web/common_api/utils.ex | 29 ---------------------- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- 8 files changed, 7 insertions(+), 61 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fe264b5e0..c214a79c5 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -208,10 +208,6 @@ defmodule Pleroma.Object do end end - def context_mapping(context) do - Object.change(%Object{}, %{data: %{"id" => context}}) - end - def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do %ObjectTombstone{ id: id, diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 57c8d1dc0..c5fb94034 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -94,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Article", "Note", "Page"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 4f8c083eb..c7e292bec 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -22,14 +22,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end def fix_object_defaults(data) do - %{data: %{"id" => context}, id: context_id} = - Utils.create_context(data["context"] || data["conversation"]) + context = Utils.maybe_create_context(data["context"] || data["conversation"]) %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"]) data |> Map.put("context", context) - |> Map.put("context_id", context_id) |> cast_and_filter_recipients("to", follower_collection) |> cast_and_filter_recipients("cc", follower_collection) |> cast_and_filter_recipients("bto", follower_collection) diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index 0e99f2037..ab204f69a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -62,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Event"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 9412be4bc..ce3305142 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -80,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Question"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 9cde7805c..d3b7d804f 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -154,22 +154,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do Notification.get_notified_from_activity(%Activity{data: object}, false) end - def create_context(context) do - context = context || generate_id("contexts") - - # Ecto has problems accessing the constraint inside the jsonb, - # so we explicitly check for the existed object before insert - object = Object.get_cached_by_ap_id(context) - - with true <- is_nil(object), - changeset <- Object.context_mapping(context), - {:ok, inserted_object} <- Repo.insert(changeset) do - inserted_object - else - _ -> - object - end - end + def maybe_create_context(context), do: context || generate_id("contexts") @doc """ Enqueues an activity for federation if it's local @@ -201,18 +186,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new("id", "pleroma:fakeid") |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", "pleroma:fakecontext") - |> Map.put_new("context_id", -1) |> lazy_put_object_defaults(true) end def lazy_put_activity_defaults(map, _fake?) do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + context = maybe_create_context(map["context"]) map |> Map.put_new_lazy("id", &generate_activity_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", context) - |> Map.put_new("context_id", context_id) |> lazy_put_object_defaults(false) end @@ -226,7 +209,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new("id", "pleroma:fake_object_id") |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", activity["context"]) - |> Map.put_new("context_id", activity["context_id"]) |> Map.put_new("fake", true) %{activity | "object" => object} @@ -239,7 +221,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new_lazy("id", &generate_object_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", activity["context"]) - |> Map.put_new("context_id", activity["context_id"]) %{activity | "object" => object} end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index ce850b038..052bd7770 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -449,35 +449,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do def get_report_statuses(_, _), do: {:ok, nil} - # DEPRECATED mostly, context objects are now created at insertion time. - def context_to_conversation_id(context) do - with %Object{id: id} <- Object.get_cached_by_ap_id(context) do - id - else - _e -> - changeset = Object.context_mapping(context) - - case Repo.insert(changeset) do - {:ok, %{id: id}} -> - id - - # This should be solved by an upsert, but it seems ecto - # has problems accessing the constraint inside the jsonb. - {:error, _} -> - Object.get_cached_by_ap_id(context).id - end - end - end - - def conversation_id_to_context(id) do - with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do - context - else - _e -> - {:error, dgettext("errors", "No such conversation")} - end - end - def validate_character_limit("" = _full_payload, [] = _attachments) do {:error, dgettext("errors", "Cannot post an empty status without attachments")} end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 1ebfd6740..31a0c420f 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -61,7 +61,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do do: context_id defp get_context_id(%{data: %{"context" => context}}) when is_binary(context), - do: Utils.context_to_conversation_id(context) + do: :erlang.crc32(context) defp get_context_id(_), do: nil -- cgit v1.2.3 From 7f71e3d0fe347920834be8c8e28d9c7f5b169e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sat, 6 Aug 2022 03:32:58 +0200 Subject: CommonFields: remove context_id --- lib/pleroma/web/activity_pub/object_validators/common_fields.ex | 2 -- lib/pleroma/web/mastodon_api/views/status_view.ex | 3 --- 2 files changed, 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 8e768ffbf..095bd0da2 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -51,8 +51,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do field(:summary, :string) field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) field(:sensitive, :boolean, default: false) field(:replies_count, :integer, default: 0) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 31a0c420f..3ffe55c5d 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -57,9 +57,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) end - defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id), - do: context_id - defp get_context_id(%{data: %{"context" => context}}) when is_binary(context), do: :erlang.crc32(context) -- cgit v1.2.3 From a9111bcaf2ba2371a1021dc171dab50615a4c040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sun, 7 Aug 2022 20:37:17 +0200 Subject: StatusView: clear MSB on calculated conversation_id This field seems to be a left-over from the StatusNet era. If your application uses `pleroma.conversation_id`: this field is deprecated. It is currently stubbed instead by doing a CRC32 of the context, and clearing the MSB to avoid overflow exceptions with signed integers on the different clients using this field (Java/Kotlin code, mostly; see Husky and probably other mobile clients.) This should be removed in a future version of Pleroma. Pleroma-FE currently depends on this field, as well. --- lib/pleroma/web/mastodon_api/views/status_view.ex | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 3ffe55c5d..5cb524f56 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -57,8 +57,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) end - defp get_context_id(%{data: %{"context" => context}}) when is_binary(context), - do: :erlang.crc32(context) + # DEPRECATED This field seems to be a left-over from the StatusNet era. + # If your application uses `pleroma.conversation_id`: this field is deprecated. + # It is currently stubbed instead by doing a CRC32 of the context, and + # clearing the MSB to avoid overflow exceptions with signed integers on the + # different clients using this field (Java/Kotlin code, mostly; see Husky.) + # This should be removed in a future version of Pleroma. Pleroma-FE currently + # depends on this field, as well. + defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do + use Bitwise + + :erlang.crc32(context) + |> band(bnot(0x8000_0000)) + end defp get_context_id(_), do: nil -- cgit v1.2.3 From 738ca484fd812d3fc027d4c3037d307c61fa24ca Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 18:15:25 -0400 Subject: Update api spec to reflect OAuth scope change --- lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') 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 82ec1e7bb..45fa2b058 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 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation 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 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do %Operation{ tags: ["Backups"], summary: "Create a backup", - security: [%{"oAuth" => ["read:account"]}], + security: [%{"oAuth" => ["read:backups"]}], operationId: "PleromaAPI.BackupController.create", responses: %{ 200 => -- cgit v1.2.3 From def0f5dc2e76b7c4ac22b393abf7f5de5e197659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sun, 7 Aug 2022 20:39:35 +0200 Subject: StatusView: implement pleroma.context field This field replaces the now deprecated conversation_id field, and now exposes the ActivityPub object `context` directly via the MastoAPI instead of relying on StatusNet-era data concepts. --- lib/pleroma/web/api_spec/schemas/status.ex | 9 ++++++++- lib/pleroma/web/mastodon_api/views/status_view.ex | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 6e6e30315..8c19a9d9f 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -142,9 +142,15 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do description: "A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`" }, + context: %Schema{ + type: :string, + description: "The thread identifier the status is associated with" + }, conversation_id: %Schema{ type: :integer, - description: "The ID of the AP context the status is associated with (if any)" + deprecated: true, + description: + "The ID of the AP context the status is associated with (if any); deprecated, please use `context` instead" }, direct_conversation_id: %Schema{ type: :integer, @@ -319,6 +325,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "pinned" => false, "pleroma" => %{ "content" => %{"text/plain" => "foobar"}, + "context" => "http://localhost:4001/objects/8b4c0c80-6a37-4d2a-b1b9-05a19e3875aa", "conversation_id" => 345_972, "direct_conversation_id" => nil, "emoji_reactions" => [], diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 5cb524f56..a4d6cd807 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -375,6 +375,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do pleroma: %{ local: activity.local, conversation_id: get_context_id(activity), + context: object.data["context"], in_reply_to_account_acct: reply_to_user && reply_to_user.nickname, content: %{"text/plain" => content_plaintext}, spoiler_text: %{"text/plain" => summary}, -- cgit v1.2.3 From 3b6784b1de8454ab8c009ac688f6c62039117742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 2 Aug 2022 17:30:36 +0200 Subject: CreateGenericValidator: fix reply context fixing Incoming Pleroma replies to a Misskey thread were rejected due to a broken context fix, which caused them to not be visible until a non-Pleroma user interacted with the replies. This fix properly sets the post-fix object context to its parent Create activity as well, if it was changed. --- .../web/activity_pub/object_validators/create_generic_validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index c9a621cb1..2395abfd4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -75,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do data |> CommonFixes.fix_actor() - |> Map.put_new("context", object["context"]) + |> Map.put("context", object["context"]) |> fix_addressing(object) end -- cgit v1.2.3 From cbdc13b76710e854c96f504526aff9da83b90ce5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 10 Aug 2022 17:09:58 -0400 Subject: Fix Varnish 7 support by ensuring Media Preview Proxy fetches headers with a capitalized HEAD verb --- lib/pleroma/web/media_proxy/media_proxy_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index 3d6716d43..d2ad62c13 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -54,7 +54,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do media_proxy_url = MediaProxy.url(url) with {:ok, %{status: status} = head_response} when status in 200..299 <- - Pleroma.HTTP.request("head", media_proxy_url, [], [], pool: :media) do + Pleroma.HTTP.request("HEAD", media_proxy_url, [], [], pool: :media) do content_type = Tesla.get_header(head_response, "content-type") content_length = Tesla.get_header(head_response, "content-length") content_length = content_length && String.to_integer(content_length) -- cgit v1.2.3 From bb02ee99f58e378e33162211f41fe5979d5da8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Wed, 10 Aug 2022 04:21:28 +0200 Subject: CommonFixes: more predictable context generation `context` fields for objects and activities can now be generated based on the object/activity `inReplyTo` field or its ActivityPub ID, as a fallback method in cases where `context` fields are missing for incoming activities and objects. --- lib/pleroma/web/activity_pub/object_validators/common_fixes.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index c7e292bec..add46d561 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -22,7 +22,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end def fix_object_defaults(data) do - context = Utils.maybe_create_context(data["context"] || data["conversation"]) + context = + Utils.maybe_create_context( + data["context"] || data["conversation"] || data["inReplyTo"] || data["id"] + ) %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"]) -- cgit v1.2.3 From 88c1c76d3eca3412d1e02008f1b8d96fe8fe0b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Mon, 15 Aug 2022 01:15:23 +0200 Subject: Migrations: delete contexts with BaseMigrator Due to the lengthiness of this task, the migration has been adapted into a BaseMigrator migration, running in the background instead. --- lib/pleroma/application.ex | 3 +- lib/pleroma/data_migration.ex | 1 + .../migrators/context_objects_deletion_migrator.ex | 139 +++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/migrators/context_objects_deletion_migrator.ex (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index d808bc732..c546713ca 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -238,7 +238,8 @@ defmodule Pleroma.Application do defp background_migrators do [ - Pleroma.Migrators.HashtagsTableMigrator + Pleroma.Migrators.HashtagsTableMigrator, + Pleroma.Migrators.ContextObjectsDeletionMigrator ] end diff --git a/lib/pleroma/data_migration.ex b/lib/pleroma/data_migration.ex index 59d891d8d..8451678fc 100644 --- a/lib/pleroma/data_migration.ex +++ b/lib/pleroma/data_migration.ex @@ -42,4 +42,5 @@ defmodule Pleroma.DataMigration do end def populate_hashtags_table, do: get_by_name("populate_hashtags_table") + def delete_context_objects, do: get_by_name("delete_context_objects") end diff --git a/lib/pleroma/migrators/context_objects_deletion_migrator.ex b/lib/pleroma/migrators/context_objects_deletion_migrator.ex new file mode 100644 index 000000000..fb224795a --- /dev/null +++ b/lib/pleroma/migrators/context_objects_deletion_migrator.ex @@ -0,0 +1,139 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Migrators.ContextObjectsDeletionMigrator do + defmodule State do + use Pleroma.Migrators.Support.BaseMigratorState + + @impl Pleroma.Migrators.Support.BaseMigratorState + defdelegate data_migration(), to: Pleroma.DataMigration, as: :delete_context_objects + end + + use Pleroma.Migrators.Support.BaseMigrator + + alias Pleroma.Migrators.Support.BaseMigrator + alias Pleroma.Object + + @doc "This migration removes objects created exclusively for contexts, containing only an `id` field." + + @impl BaseMigrator + def feature_config_path, do: [:features, :delete_context_objects] + + @impl BaseMigrator + def fault_rate_allowance, do: Config.get([:delete_context_objects, :fault_rate_allowance], 0) + + @impl BaseMigrator + def perform do + data_migration_id = data_migration_id() + max_processed_id = get_stat(:max_processed_id, 0) + + Logger.info("Deleting context objects from `objects` (from oid: #{max_processed_id})...") + + query() + |> where([object], object.id > ^max_processed_id) + |> Repo.chunk_stream(100, :batches, timeout: :infinity) + |> Stream.each(fn objects -> + object_ids = Enum.map(objects, & &1.id) + + results = Enum.map(object_ids, &delete_context_object(&1)) + + failed_ids = + results + |> Enum.filter(&(elem(&1, 0) == :error)) + |> Enum.map(&elem(&1, 1)) + + chunk_affected_count = + results + |> Enum.filter(&(elem(&1, 0) == :ok)) + |> length() + + for failed_id <- failed_ids do + _ = + Repo.query( + "INSERT INTO data_migration_failed_ids(data_migration_id, record_id) " <> + "VALUES ($1, $2) ON CONFLICT DO NOTHING;", + [data_migration_id, failed_id] + ) + end + + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = ANY($2)", + [data_migration_id, object_ids -- failed_ids] + ) + + max_object_id = Enum.at(object_ids, -1) + + put_stat(:max_processed_id, max_object_id) + increment_stat(:iteration_processed_count, length(object_ids)) + increment_stat(:processed_count, length(object_ids)) + increment_stat(:failed_count, length(failed_ids)) + increment_stat(:affected_count, chunk_affected_count) + put_stat(:records_per_second, records_per_second()) + persist_state() + + # A quick and dirty approach to controlling the load this background migration imposes + sleep_interval = Config.get([:delete_context_objects, :sleep_interval_ms], 0) + Process.sleep(sleep_interval) + end) + |> Stream.run() + end + + @impl BaseMigrator + def query do + # Context objects have no activity type, and only one field, `id`. + # Only those context objects are without types. + from( + object in Object, + where: fragment("(?)->'type' IS NULL", object.data), + select: %{ + id: object.id + } + ) + end + + @spec delete_context_object(integer()) :: {:ok | :error, integer()} + defp delete_context_object(id) do + result = + %Object{id: id} + |> Repo.delete() + |> elem(0) + + {result, id} + end + + @impl BaseMigrator + def retry_failed do + data_migration_id = data_migration_id() + + failed_objects_query() + |> Repo.chunk_stream(100, :one) + |> Stream.each(fn object -> + with {res, _} when res != :error <- delete_context_object(object.id) do + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = $2", + [data_migration_id, object.id] + ) + end + end) + |> Stream.run() + + put_stat(:failed_count, failures_count()) + persist_state() + + force_continue() + end + + defp failed_objects_query do + from(o in Object) + |> join(:inner, [o], dmf in fragment("SELECT * FROM data_migration_failed_ids"), + on: dmf.record_id == o.id + ) + |> where([_o, dmf], dmf.data_migration_id == ^data_migration_id()) + |> order_by([o], asc: o.id) + end +end -- cgit v1.2.3 From f41d970a592568956aa97959f28cb89cadf5f2bc Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Mon, 18 Jul 2022 15:21:27 +0100 Subject: fix resolution of GTS user keys --- lib/pleroma/signature.ex | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index dbe6fd209..ff0c56856 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -10,17 +10,14 @@ defmodule Pleroma.Signature do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + @known_suffixes ["/publickey", "/main-key"] + def key_id_to_actor_id(key_id) do uri = - URI.parse(key_id) + key_id + |> URI.parse() |> Map.put(:fragment, nil) - - uri = - if not is_nil(uri.path) and String.ends_with?(uri.path, "/publickey") do - Map.put(uri, :path, String.replace(uri.path, "/publickey", "")) - else - uri - end + |> remove_suffix(@known_suffixes) maybe_ap_id = URI.to_string(uri) @@ -36,6 +33,16 @@ defmodule Pleroma.Signature do end end + defp remove_suffix(uri, [test | rest]) do + if not is_nil(uri.path) and String.ends_with?(uri.path, test) do + Map.put(uri, :path, String.replace(uri.path, test, "")) + else + remove_suffix(uri, rest) + end + end + + defp remove_suffix(uri, []), do: uri + def fetch_public_key(conn) do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), {:ok, actor_id} <- key_id_to_actor_id(kid), -- cgit v1.2.3 From 61254111e59f02118cad15de49d1e0704c07030e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Wed, 17 Aug 2022 03:30:02 +0200 Subject: HttpSignaturePlug: accept standard (request-target) The (request-target) used by Pleroma is non-standard, but many HTTP signature implementations do it this way due to a misinterpretation of the draft 06 of HTTP signatures: "path" was interpreted as not having the query, though later examples show that it must be the absolute path with the query part of the URL as well. This behavior is kept to make sure most software (Pleroma itself, Mastodon, and probably others) do not break, but Pleroma now accepts signatures for a (request-target) containing the query, as expected by many HTTP signature libraries, and clarified in the draft 11 of HTTP signatures. Additionally, the new draft renamed (request-target) to @request-target. We now support both for incoming requests' signatures. --- lib/pleroma/web/plugs/http_signature_plug.ex | 53 +++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index d023754a6..4bf325218 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -25,21 +25,58 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end end + defp validate_signature(conn, request_target) do + # Newer drafts for HTTP signatures now use @request-target instead of the + # old (request-target). We'll now support both for incoming signatures. + conn = + conn + |> put_req_header("(request-target)", request_target) + |> put_req_header("@request-target", request_target) + + HTTPSignatures.validate_conn(conn) + end + + defp validate_signature(conn) do + # This (request-target) is non-standard, but many implementations do it + # this way due to a misinterpretation of + # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06 + # "path" was interpreted as not having the query, though later examples + # show that it must be the absolute path + query. This behavior is kept to + # make sure most software (Pleroma itself, Mastodon, and probably others) + # do not break. + request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" + + # This is the proper way to build the @request-target, as expected by + # many HTTP signature libraries, clarified in the following draft: + # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6 + # It is the same as before, but containing the query part as well. + proper_target = request_target <> "?#{conn.query_string}" + + cond do + # Normal, non-standard behavior but expected by Pleroma and more. + validate_signature(conn, request_target) -> + true + + # Has query string and the previous one failed: let's try the standard. + conn.query_string != "" -> + validate_signature(conn, proper_target) + + # If there's no query string and signature fails, it's rotten. + true -> + false + end + end + defp maybe_assign_valid_signature(conn) do if has_signature_header?(conn) do - # set (request-target) header to the appropriate value - # we also replace the digest header with the one we computed - request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" - + # we replace the digest header with the one we computed in DigestPlug conn = - conn - |> put_req_header("(request-target)", request_target) - |> case do + case conn do %{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest) conn -> conn end - assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn)) + assign(conn, :valid_signature, validate_signature(conn)) else Logger.debug("No signature header!") conn -- cgit v1.2.3 From 4661b56720b4f70eb6996bf975c4d88db9828006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Fri, 19 Aug 2022 02:45:49 +0200 Subject: ArticleNotePageValidator: fix replies fixing Some software, like GoToSocial, expose replies as ActivityPub Collections, but do not expose any item array directly in the object, causing validation to fail via the ObjectID validator. Now, Pleroma will drop that field in this situation too. --- .../activity_pub/object_validators/article_note_page_validator.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 57c8d1dc0..4243e0fbf 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -60,7 +60,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies), do: Map.put(data, "replies", replies) - defp fix_replies(%{"replies" => replies} = data) when is_bitstring(replies), + # TODO: Pleroma does not have any support for Collections at the moment. + # If the `replies` field is not something the ObjectID validator can handle, + # the activity/object would be rejected, which is bad behavior. + defp fix_replies(%{"replies" => replies} = data) when not is_list(replies), do: Map.drop(data, ["replies"]) defp fix_replies(data), do: data -- cgit v1.2.3 From 0cee3c6e937ce7b15392a7abc5bbc30bfc80e7f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Sat, 20 Aug 2022 00:21:07 +0200 Subject: emoji-test: update to latest 15.0 draft --- lib/pleroma/emoji-test.txt | 125 ++++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 46 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji-test.txt b/lib/pleroma/emoji-test.txt index dd5493366..87d093d64 100644 --- a/lib/pleroma/emoji-test.txt +++ b/lib/pleroma/emoji-test.txt @@ -1,13 +1,13 @@ # emoji-test.txt -# Date: 2021-08-26, 17:22:23 GMT -# © 2021 Unicode®, Inc. +# Date: 2022-08-12, 20:24:39 GMT +# © 2022 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. -# For terms of use, see http://www.unicode.org/terms_of_use.html +# For terms of use, see https://www.unicode.org/terms_of_use.html # # Emoji Keyboard/Display Test Data for UTS #51 -# Version: 14.0 +# Version: 15.0 # -# For documentation and usage, see http://www.unicode.org/reports/tr51 +# For documentation and usage, see https://www.unicode.org/reports/tr51 # # This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed. # Format: code points; status # emoji name @@ -92,6 +92,7 @@ 1F62C ; fully-qualified # 😬 E1.0 grimacing face 1F62E 200D 1F4A8 ; fully-qualified # 😮‍💨 E13.1 face exhaling 1F925 ; fully-qualified # 🤥 E3.0 lying face +1FAE8 ; fully-qualified # 🫨 E15.0 shaking face # subgroup: face-sleepy 1F60C ; fully-qualified # 😌 E0.6 relieved face @@ -155,7 +156,7 @@ # subgroup: face-negative 1F624 ; fully-qualified # 😤 E0.6 face with steam from nose -1F621 ; fully-qualified # 😡 E0.6 pouting face +1F621 ; fully-qualified # 😡 E0.6 enraged face 1F620 ; fully-qualified # 😠 E0.6 angry face 1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth 1F608 ; fully-qualified # 😈 E1.0 smiling face with horns @@ -190,8 +191,7 @@ 1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey 1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey -# subgroup: emotion -1F48B ; fully-qualified # 💋 E0.6 kiss mark +# subgroup: heart 1F48C ; fully-qualified # 💌 E0.6 love letter 1F498 ; fully-qualified # 💘 E0.6 heart with arrow 1F49D ; fully-qualified # 💝 E0.6 heart with ribbon @@ -210,14 +210,20 @@ 2764 200D 1FA79 ; unqualified # ❤‍🩹 E13.1 mending heart 2764 FE0F ; fully-qualified # ❤️ E0.6 red heart 2764 ; unqualified # ❤ E0.6 red heart +1FA77 ; fully-qualified # 🩷 E15.0 pink heart 1F9E1 ; fully-qualified # 🧡 E5.0 orange heart 1F49B ; fully-qualified # 💛 E0.6 yellow heart 1F49A ; fully-qualified # 💚 E0.6 green heart 1F499 ; fully-qualified # 💙 E0.6 blue heart +1FA75 ; fully-qualified # 🩵 E15.0 light blue heart 1F49C ; fully-qualified # 💜 E0.6 purple heart 1F90E ; fully-qualified # 🤎 E12.0 brown heart 1F5A4 ; fully-qualified # 🖤 E3.0 black heart +1FA76 ; fully-qualified # 🩶 E15.0 grey heart 1F90D ; fully-qualified # 🤍 E12.0 white heart + +# subgroup: emotion +1F48B ; fully-qualified # 💋 E0.6 kiss mark 1F4AF ; fully-qualified # 💯 E0.6 hundred points 1F4A2 ; fully-qualified # 💢 E0.6 anger symbol 1F4A5 ; fully-qualified # 💥 E0.6 collision @@ -226,21 +232,20 @@ 1F4A8 ; fully-qualified # 💨 E0.6 dashing away 1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole 1F573 ; unqualified # 🕳 E0.7 hole -1F4A3 ; fully-qualified # 💣 E0.6 bomb 1F4AC ; fully-qualified # 💬 E0.6 speech balloon 1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️‍🗨️ E2.0 eye in speech bubble 1F441 200D 1F5E8 FE0F ; unqualified # 👁‍🗨️ E2.0 eye in speech bubble -1F441 FE0F 200D 1F5E8 ; unqualified # 👁️‍🗨 E2.0 eye in speech bubble +1F441 FE0F 200D 1F5E8 ; minimally-qualified # 👁️‍🗨 E2.0 eye in speech bubble 1F441 200D 1F5E8 ; unqualified # 👁‍🗨 E2.0 eye in speech bubble 1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble 1F5E8 ; unqualified # 🗨 E2.0 left speech bubble 1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble 1F5EF ; unqualified # 🗯 E0.7 right anger bubble 1F4AD ; fully-qualified # 💭 E1.0 thought balloon -1F4A4 ; fully-qualified # 💤 E0.6 zzz +1F4A4 ; fully-qualified # 💤 E0.6 ZZZ -# Smileys & Emotion subtotal: 177 -# Smileys & Emotion subtotal: 177 w/o modifiers +# Smileys & Emotion subtotal: 180 +# Smileys & Emotion subtotal: 180 w/o modifiers # group: People & Body @@ -300,6 +305,18 @@ 1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium skin tone 1FAF4 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone 1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone +1FAF7 ; fully-qualified # 🫷 E15.0 leftwards pushing hand +1FAF7 1F3FB ; fully-qualified # 🫷🏻 E15.0 leftwards pushing hand: light skin tone +1FAF7 1F3FC ; fully-qualified # 🫷🏼 E15.0 leftwards pushing hand: medium-light skin tone +1FAF7 1F3FD ; fully-qualified # 🫷🏽 E15.0 leftwards pushing hand: medium skin tone +1FAF7 1F3FE ; fully-qualified # 🫷🏾 E15.0 leftwards pushing hand: medium-dark skin tone +1FAF7 1F3FF ; fully-qualified # 🫷🏿 E15.0 leftwards pushing hand: dark skin tone +1FAF8 ; fully-qualified # 🫸 E15.0 rightwards pushing hand +1FAF8 1F3FB ; fully-qualified # 🫸🏻 E15.0 rightwards pushing hand: light skin tone +1FAF8 1F3FC ; fully-qualified # 🫸🏼 E15.0 rightwards pushing hand: medium-light skin tone +1FAF8 1F3FD ; fully-qualified # 🫸🏽 E15.0 rightwards pushing hand: medium skin tone +1FAF8 1F3FE ; fully-qualified # 🫸🏾 E15.0 rightwards pushing hand: medium-dark skin tone +1FAF8 1F3FF ; fully-qualified # 🫸🏿 E15.0 rightwards pushing hand: dark skin tone # subgroup: hand-fingers-partial 1F44C ; fully-qualified # 👌 E0.6 OK hand @@ -473,11 +490,11 @@ 1F932 1F3FE ; fully-qualified # 🤲🏾 E5.0 palms up together: medium-dark skin tone 1F932 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone 1F91D ; fully-qualified # 🤝 E3.0 handshake -1F91D 1F3FB ; fully-qualified # 🤝🏻 E3.0 handshake: light skin tone -1F91D 1F3FC ; fully-qualified # 🤝🏼 E3.0 handshake: medium-light skin tone -1F91D 1F3FD ; fully-qualified # 🤝🏽 E3.0 handshake: medium skin tone -1F91D 1F3FE ; fully-qualified # 🤝🏾 E3.0 handshake: medium-dark skin tone -1F91D 1F3FF ; fully-qualified # 🤝🏿 E3.0 handshake: dark skin tone +1F91D 1F3FB ; fully-qualified # 🤝🏻 E14.0 handshake: light skin tone +1F91D 1F3FC ; fully-qualified # 🤝🏼 E14.0 handshake: medium-light skin tone +1F91D 1F3FD ; fully-qualified # 🤝🏽 E14.0 handshake: medium skin tone +1F91D 1F3FE ; fully-qualified # 🤝🏾 E14.0 handshake: medium-dark skin tone +1F91D 1F3FF ; fully-qualified # 🤝🏿 E14.0 handshake: dark skin tone 1FAF1 1F3FB 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏻‍🫲🏼 E14.0 handshake: light skin tone, medium-light skin tone 1FAF1 1F3FB 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏻‍🫲🏽 E14.0 handshake: light skin tone, medium skin tone 1FAF1 1F3FB 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏻‍🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone @@ -1455,7 +1472,7 @@ 1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone 1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️‍♂️ E4.0 man detective 1F575 200D 2642 FE0F ; unqualified # 🕵‍♂️ E4.0 man detective -1F575 FE0F 200D 2642 ; unqualified # 🕵️‍♂ E4.0 man detective +1F575 FE0F 200D 2642 ; minimally-qualified # 🕵️‍♂ E4.0 man detective 1F575 200D 2642 ; unqualified # 🕵‍♂ E4.0 man detective 1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻‍♂️ E4.0 man detective: light skin tone 1F575 1F3FB 200D 2642 ; minimally-qualified # 🕵🏻‍♂ E4.0 man detective: light skin tone @@ -1469,7 +1486,7 @@ 1F575 1F3FF 200D 2642 ; minimally-qualified # 🕵🏿‍♂ E4.0 man detective: dark skin tone 1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️‍♀️ E4.0 woman detective 1F575 200D 2640 FE0F ; unqualified # 🕵‍♀️ E4.0 woman detective -1F575 FE0F 200D 2640 ; unqualified # 🕵️‍♀ E4.0 woman detective +1F575 FE0F 200D 2640 ; minimally-qualified # 🕵️‍♀ E4.0 woman detective 1F575 200D 2640 ; unqualified # 🕵‍♀ E4.0 woman detective 1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻‍♀️ E4.0 woman detective: light skin tone 1F575 1F3FB 200D 2640 ; minimally-qualified # 🕵🏻‍♀ E4.0 woman detective: light skin tone @@ -2302,7 +2319,7 @@ 1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone 1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️‍♂️ E4.0 man golfing 1F3CC 200D 2642 FE0F ; unqualified # 🏌‍♂️ E4.0 man golfing -1F3CC FE0F 200D 2642 ; unqualified # 🏌️‍♂ E4.0 man golfing +1F3CC FE0F 200D 2642 ; minimally-qualified # 🏌️‍♂ E4.0 man golfing 1F3CC 200D 2642 ; unqualified # 🏌‍♂ E4.0 man golfing 1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻‍♂️ E4.0 man golfing: light skin tone 1F3CC 1F3FB 200D 2642 ; minimally-qualified # 🏌🏻‍♂ E4.0 man golfing: light skin tone @@ -2316,7 +2333,7 @@ 1F3CC 1F3FF 200D 2642 ; minimally-qualified # 🏌🏿‍♂ E4.0 man golfing: dark skin tone 1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️‍♀️ E4.0 woman golfing 1F3CC 200D 2640 FE0F ; unqualified # 🏌‍♀️ E4.0 woman golfing -1F3CC FE0F 200D 2640 ; unqualified # 🏌️‍♀ E4.0 woman golfing +1F3CC FE0F 200D 2640 ; minimally-qualified # 🏌️‍♀ E4.0 woman golfing 1F3CC 200D 2640 ; unqualified # 🏌‍♀ E4.0 woman golfing 1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻‍♀️ E4.0 woman golfing: light skin tone 1F3CC 1F3FB 200D 2640 ; minimally-qualified # 🏌🏻‍♀ E4.0 woman golfing: light skin tone @@ -2427,7 +2444,7 @@ 26F9 1F3FF ; fully-qualified # ⛹🏿 E2.0 person bouncing ball: dark skin tone 26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️‍♂️ E4.0 man bouncing ball 26F9 200D 2642 FE0F ; unqualified # ⛹‍♂️ E4.0 man bouncing ball -26F9 FE0F 200D 2642 ; unqualified # ⛹️‍♂ E4.0 man bouncing ball +26F9 FE0F 200D 2642 ; minimally-qualified # ⛹️‍♂ E4.0 man bouncing ball 26F9 200D 2642 ; unqualified # ⛹‍♂ E4.0 man bouncing ball 26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻‍♂️ E4.0 man bouncing ball: light skin tone 26F9 1F3FB 200D 2642 ; minimally-qualified # ⛹🏻‍♂ E4.0 man bouncing ball: light skin tone @@ -2441,7 +2458,7 @@ 26F9 1F3FF 200D 2642 ; minimally-qualified # ⛹🏿‍♂ E4.0 man bouncing ball: dark skin tone 26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️‍♀️ E4.0 woman bouncing ball 26F9 200D 2640 FE0F ; unqualified # ⛹‍♀️ E4.0 woman bouncing ball -26F9 FE0F 200D 2640 ; unqualified # ⛹️‍♀ E4.0 woman bouncing ball +26F9 FE0F 200D 2640 ; minimally-qualified # ⛹️‍♀ E4.0 woman bouncing ball 26F9 200D 2640 ; unqualified # ⛹‍♀ E4.0 woman bouncing ball 26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻‍♀️ E4.0 woman bouncing ball: light skin tone 26F9 1F3FB 200D 2640 ; minimally-qualified # ⛹🏻‍♀ E4.0 woman bouncing ball: light skin tone @@ -2462,7 +2479,7 @@ 1F3CB 1F3FF ; fully-qualified # 🏋🏿 E2.0 person lifting weights: dark skin tone 1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️‍♂️ E4.0 man lifting weights 1F3CB 200D 2642 FE0F ; unqualified # 🏋‍♂️ E4.0 man lifting weights -1F3CB FE0F 200D 2642 ; unqualified # 🏋️‍♂ E4.0 man lifting weights +1F3CB FE0F 200D 2642 ; minimally-qualified # 🏋️‍♂ E4.0 man lifting weights 1F3CB 200D 2642 ; unqualified # 🏋‍♂ E4.0 man lifting weights 1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻‍♂️ E4.0 man lifting weights: light skin tone 1F3CB 1F3FB 200D 2642 ; minimally-qualified # 🏋🏻‍♂ E4.0 man lifting weights: light skin tone @@ -2476,7 +2493,7 @@ 1F3CB 1F3FF 200D 2642 ; minimally-qualified # 🏋🏿‍♂ E4.0 man lifting weights: dark skin tone 1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️‍♀️ E4.0 woman lifting weights 1F3CB 200D 2640 FE0F ; unqualified # 🏋‍♀️ E4.0 woman lifting weights -1F3CB FE0F 200D 2640 ; unqualified # 🏋️‍♀ E4.0 woman lifting weights +1F3CB FE0F 200D 2640 ; minimally-qualified # 🏋️‍♀ E4.0 woman lifting weights 1F3CB 200D 2640 ; unqualified # 🏋‍♀ E4.0 woman lifting weights 1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻‍♀️ E4.0 woman lifting weights: light skin tone 1F3CB 1F3FB 200D 2640 ; minimally-qualified # 🏋🏻‍♀ E4.0 woman lifting weights: light skin tone @@ -3262,8 +3279,8 @@ 1FAC2 ; fully-qualified # 🫂 E13.0 people hugging 1F463 ; fully-qualified # 👣 E0.6 footprints -# People & Body subtotal: 2986 -# People & Body subtotal: 506 w/o modifiers +# People & Body subtotal: 2998 +# People & Body subtotal: 508 w/o modifiers # group: Component @@ -3306,6 +3323,8 @@ 1F405 ; fully-qualified # 🐅 E1.0 tiger 1F406 ; fully-qualified # 🐆 E1.0 leopard 1F434 ; fully-qualified # 🐴 E0.6 horse face +1FACE ; fully-qualified # 🫎 E15.0 moose +1FACF ; fully-qualified # 🫏 E15.0 donkey 1F40E ; fully-qualified # 🐎 E0.6 horse 1F984 ; fully-qualified # 🦄 E1.0 unicorn 1F993 ; fully-qualified # 🦓 E5.0 zebra @@ -3373,6 +3392,9 @@ 1F9A9 ; fully-qualified # 🦩 E12.0 flamingo 1F99A ; fully-qualified # 🦚 E11.0 peacock 1F99C ; fully-qualified # 🦜 E11.0 parrot +1FABD ; fully-qualified # 🪽 E15.0 wing +1F426 200D 2B1B ; fully-qualified # 🐦‍⬛ E15.0 black bird +1FABF ; fully-qualified # 🪿 E15.0 goose # subgroup: animal-amphibian 1F438 ; fully-qualified # 🐸 E0.6 frog @@ -3399,6 +3421,7 @@ 1F419 ; fully-qualified # 🐙 E0.6 octopus 1F41A ; fully-qualified # 🐚 E0.6 spiral shell 1FAB8 ; fully-qualified # 🪸 E14.0 coral +1FABC ; fully-qualified # 🪼 E15.0 jellyfish # subgroup: animal-bug 1F40C ; fully-qualified # 🐌 E0.6 snail @@ -3433,6 +3456,7 @@ 1F33B ; fully-qualified # 🌻 E0.6 sunflower 1F33C ; fully-qualified # 🌼 E0.6 blossom 1F337 ; fully-qualified # 🌷 E0.6 tulip +1FABB ; fully-qualified # 🪻 E15.0 hyacinth # subgroup: plant-other 1F331 ; fully-qualified # 🌱 E0.6 seedling @@ -3451,9 +3475,10 @@ 1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind 1FAB9 ; fully-qualified # 🪹 E14.0 empty nest 1FABA ; fully-qualified # 🪺 E14.0 nest with eggs +1F344 ; fully-qualified # 🍄 E0.6 mushroom -# Animals & Nature subtotal: 151 -# Animals & Nature subtotal: 151 w/o modifiers +# Animals & Nature subtotal: 159 +# Animals & Nature subtotal: 159 w/o modifiers # group: Food & Drink @@ -3492,10 +3517,11 @@ 1F966 ; fully-qualified # 🥦 E5.0 broccoli 1F9C4 ; fully-qualified # 🧄 E12.0 garlic 1F9C5 ; fully-qualified # 🧅 E12.0 onion -1F344 ; fully-qualified # 🍄 E0.6 mushroom 1F95C ; fully-qualified # 🥜 E3.0 peanuts 1FAD8 ; fully-qualified # 🫘 E14.0 beans 1F330 ; fully-qualified # 🌰 E0.6 chestnut +1FADA ; fully-qualified # 🫚 E15.0 ginger root +1FADB ; fully-qualified # 🫛 E15.0 pea pod # subgroup: food-prepared 1F35E ; fully-qualified # 🍞 E0.6 bread @@ -3607,8 +3633,8 @@ 1FAD9 ; fully-qualified # 🫙 E14.0 jar 1F3FA ; fully-qualified # 🏺 E1.0 amphora -# Food & Drink subtotal: 134 -# Food & Drink subtotal: 134 w/o modifiers +# Food & Drink subtotal: 135 +# Food & Drink subtotal: 135 w/o modifiers # group: Travel & Places @@ -3974,11 +4000,10 @@ 1F3AF ; fully-qualified # 🎯 E0.6 bullseye 1FA80 ; fully-qualified # 🪀 E12.0 yo-yo 1FA81 ; fully-qualified # 🪁 E12.0 kite +1F52B ; fully-qualified # 🔫 E0.6 water pistol 1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball 1F52E ; fully-qualified # 🔮 E0.6 crystal ball 1FA84 ; fully-qualified # 🪄 E13.0 magic wand -1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet -1FAAC ; fully-qualified # 🪬 E14.0 hamsa 1F3AE ; fully-qualified # 🎮 E0.6 video game 1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick 1F579 ; unqualified # 🕹 E0.7 joystick @@ -4013,8 +4038,8 @@ 1F9F6 ; fully-qualified # 🧶 E11.0 yarn 1FAA2 ; fully-qualified # 🪢 E13.0 knot -# Activities subtotal: 97 -# Activities subtotal: 97 w/o modifiers +# Activities subtotal: 96 +# Activities subtotal: 96 w/o modifiers # group: Objects @@ -4040,6 +4065,7 @@ 1FA73 ; fully-qualified # 🩳 E12.0 shorts 1F459 ; fully-qualified # 👙 E0.6 bikini 1F45A ; fully-qualified # 👚 E0.6 woman’s clothes +1FAAD ; fully-qualified # 🪭 E15.0 folding hand fan 1F45B ; fully-qualified # 👛 E0.6 purse 1F45C ; fully-qualified # 👜 E0.6 handbag 1F45D ; fully-qualified # 👝 E0.6 clutch bag @@ -4055,6 +4081,7 @@ 1F461 ; fully-qualified # 👡 E0.6 woman’s sandal 1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes 1F462 ; fully-qualified # 👢 E0.6 woman’s boot +1FAAE ; fully-qualified # 🪮 E15.0 hair pick 1F451 ; fully-qualified # 👑 E0.6 crown 1F452 ; fully-qualified # 👒 E0.6 woman’s hat 1F3A9 ; fully-qualified # 🎩 E0.6 top hat @@ -4103,6 +4130,8 @@ 1FA95 ; fully-qualified # 🪕 E12.0 banjo 1F941 ; fully-qualified # 🥁 E3.0 drum 1FA98 ; fully-qualified # 🪘 E13.0 long drum +1FA87 ; fully-qualified # 🪇 E15.0 maracas +1FA88 ; fully-qualified # 🪈 E15.0 flute # subgroup: phone 1F4F1 ; fully-qualified # 📱 E0.6 mobile phone @@ -4275,7 +4304,7 @@ 1F5E1 ; unqualified # 🗡 E0.7 dagger 2694 FE0F ; fully-qualified # ⚔️ E1.0 crossed swords 2694 ; unqualified # ⚔ E1.0 crossed swords -1F52B ; fully-qualified # 🔫 E0.6 water pistol +1F4A3 ; fully-qualified # 💣 E0.6 bomb 1FA83 ; fully-qualified # 🪃 E13.0 boomerang 1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow 1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield @@ -4354,12 +4383,14 @@ 1FAA6 ; fully-qualified # 🪦 E13.0 headstone 26B1 FE0F ; fully-qualified # ⚱️ E1.0 funeral urn 26B1 ; unqualified # ⚱ E1.0 funeral urn +1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet +1FAAC ; fully-qualified # 🪬 E14.0 hamsa 1F5FF ; fully-qualified # 🗿 E0.6 moai 1FAA7 ; fully-qualified # 🪧 E13.0 placard 1FAAA ; fully-qualified # 🪪 E14.0 identification card -# Objects subtotal: 304 -# Objects subtotal: 304 w/o modifiers +# Objects subtotal: 310 +# Objects subtotal: 310 w/o modifiers # group: Symbols @@ -4455,6 +4486,7 @@ 262E ; unqualified # ☮ E1.0 peace symbol 1F54E ; fully-qualified # 🕎 E1.0 menorah 1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star +1FAAF ; fully-qualified # 🪯 E15.0 khanda # subgroup: zodiac 2648 ; fully-qualified # ♈ E0.6 Aries @@ -4503,6 +4535,7 @@ 1F505 ; fully-qualified # 🔅 E1.0 dim button 1F506 ; fully-qualified # 🔆 E1.0 bright button 1F4F6 ; fully-qualified # 📶 E0.6 antenna bars +1F6DC ; fully-qualified # 🛜 E15.0 wireless 1F4F3 ; fully-qualified # 📳 E0.6 vibration mode 1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off @@ -4693,8 +4726,8 @@ 1F533 ; fully-qualified # 🔳 E0.6 white square button 1F532 ; fully-qualified # 🔲 E0.6 black square button -# Symbols subtotal: 302 -# Symbols subtotal: 302 w/o modifiers +# Symbols subtotal: 304 +# Symbols subtotal: 304 w/o modifiers # group: Flags @@ -4709,7 +4742,7 @@ 1F3F3 200D 1F308 ; unqualified # 🏳‍🌈 E4.0 rainbow flag 1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️‍⚧️ E13.0 transgender flag 1F3F3 200D 26A7 FE0F ; unqualified # 🏳‍⚧️ E13.0 transgender flag -1F3F3 FE0F 200D 26A7 ; unqualified # 🏳️‍⚧ E13.0 transgender flag +1F3F3 FE0F 200D 26A7 ; minimally-qualified # 🏳️‍⚧ E13.0 transgender flag 1F3F3 200D 26A7 ; unqualified # 🏳‍⚧ E13.0 transgender flag 1F3F4 200D 2620 FE0F ; fully-qualified # 🏴‍☠️ E11.0 pirate flag 1F3F4 200D 2620 ; minimally-qualified # 🏴‍☠ E11.0 pirate flag @@ -4983,9 +5016,9 @@ # Flags subtotal: 275 w/o modifiers # Status Counts -# fully-qualified : 3624 -# minimally-qualified : 817 -# unqualified : 252 +# fully-qualified : 3655 +# minimally-qualified : 827 +# unqualified : 242 # component : 9 #EOF -- cgit v1.2.3 From c62a4f1c173490ad64fdfbab0c005ca3523b6013 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 13:19:38 -0400 Subject: Disconnect streaming sessions when token is revoked --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 8 ++++++-- lib/pleroma/web/o_auth/token/strategy/revoke.ex | 1 + lib/pleroma/web/streamer.ex | 24 +++++++++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 0d1faffbd..ffbc2c4de 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler 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 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler 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 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler 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 8d6572704..03a0b91ae 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 @@ defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke 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 fc3bbb130..8bf70d99b 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.Streamer do {: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 @@ defmodule Pleroma.Web.Streamer 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 @@ defmodule Pleroma.Web.Streamer 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. -- cgit v1.2.3 From f459c1260b43396fb7173e97e29ccef441a615ec Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 14:10:07 -0400 Subject: Lint --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index ffbc2c4de..930e9eb29 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -32,7 +32,8 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler 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} -> -- cgit v1.2.3 From a31d6bb52c8856c71f20d49aec8948573dacba68 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 19 Aug 2022 14:58:57 -0400 Subject: 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(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 9824e0a4a..92d143665 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -89,7 +89,8 @@ defmodule Pleroma.Application 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 03a0b91ae..de99bc137 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 @@ defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke 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 -- cgit v1.2.3 From 3885ee182a572a10b326ae553703ee0d38f3b66d Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 11 Jul 2022 15:49:58 -0400 Subject: Switch to associated_object_id index --- lib/mix/tasks/pleroma/database.ex | 3 +-- lib/pleroma/activity.ex | 5 ++--- lib/pleroma/activity/queries.ex | 6 ++---- lib/pleroma/migrators/hashtags_table_migrator.ex | 2 +- lib/pleroma/notification.ex | 9 +++------ lib/pleroma/object.ex | 3 +-- lib/pleroma/web/activity_pub/activity_pub.ex | 3 +-- 7 files changed, 11 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 6b8f0ef68..ed560c177 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -154,9 +154,8 @@ defmodule Mix.Tasks.Pleroma.Database do |> join(:inner, [a], o in Object, on: fragment( - "(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + "(?->>'id') = associated_object_id((?))", o.data, - a.data, a.data ) ) diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 12c1a3b2e..ebfd4ed45 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -53,7 +53,7 @@ defmodule Pleroma.Activity do # # ``` # |> join(:inner, [activity], o in Object, - # on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + # on: fragment("(?->>'id') = associated_object_id((?))", # o.data, activity.data, activity.data)) # |> preload([activity, object], [object: object]) # ``` @@ -69,9 +69,8 @@ defmodule Pleroma.Activity do join(query, join_type, [activity], o in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", o.data, - activity.data, activity.data ), as: :object diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index a898b2ea7..81c44ac05 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -52,8 +52,7 @@ defmodule Pleroma.Activity.Queries do activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", - activity.data, + "associated_object_id((?)) = ANY(?)", activity.data, ^object_ids ) @@ -64,8 +63,7 @@ defmodule Pleroma.Activity.Queries do from(activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, + "associated_object_id((?)) = ?", activity.data, ^object_id ) diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex index fa1190b7d..dca4bfa6f 100644 --- a/lib/pleroma/migrators/hashtags_table_migrator.ex +++ b/lib/pleroma/migrators/hashtags_table_migrator.ex @@ -183,7 +183,7 @@ defmodule Pleroma.Migrators.HashtagsTableMigrator do DELETE FROM hashtags_objects WHERE object_id IN (SELECT DISTINCT objects.id FROM objects JOIN hashtags_objects ON hashtags_objects.object_id = objects.id LEFT JOIN activities - ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = + ON associated_object_id(activities) = (objects.data->>'id') AND activities.data->>'type' = 'Create' WHERE activities.id IS NULL); diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 52fd2656b..76d2d5ece 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -117,9 +117,8 @@ defmodule Pleroma.Notification do |> join(:left, [n, a], object in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", object.data, - a.data, a.data ) ) @@ -193,13 +192,11 @@ defmodule Pleroma.Notification do |> join(:left, [n, a], mutated_activity in Pleroma.Activity, on: fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - a.data, + "associated_object_id(?)", a.data ) == fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - mutated_activity.data, + "associated_object_id(?)", mutated_activity.data ) and fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fe264b5e0..e7d0d52b0 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -40,8 +40,7 @@ defmodule Pleroma.Object do join(query, join_type, [{object, object_position}], a in Activity, on: fragment( - "COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ", - a.data, + "associated_object_id(?) = (? ->> 'id') AND (?->>'type' = ?) ", a.data, object.data, a.data, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bded254c6..07b0a92a4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1150,8 +1150,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do [activity, object: o] in query, where: fragment( - "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)", - activity.data, + "(?)->>'type' = 'Create' and associated_object_id((?)) = any (?)", activity.data, activity.data, ^ids -- cgit v1.2.3 From 27016287862a93b1fb4a4bebda3199e32c46d962 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 15:01:37 -0500 Subject: Add remote interaction ui for posts --- .../twitter_api/util/status_interact.html.eex | 13 ++++++ .../web/twitter_api/controllers/util_controller.ex | 47 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex (limited to 'lib') diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex new file mode 100644 index 000000000..bb3d0a0af --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -0,0 +1,13 @@ +<%= if @error do %> +

Error: <%= @error %>

+<% else %> +

Interacting with <%= @nickname %>

+
+ <%= @status_id %> +
+ <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> + <%= hidden_input f, :status, value: @status_id %> + <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> + <%= submit "Interact" %> + <% end %> +<% end %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 5731c78a8..ee99aab3e 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do require Logger + alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Emoji alias Pleroma.Healthcheck @@ -59,6 +60,27 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + with %Activity{} = activity <- Activity.get_by_id(id), + %User{} = user <- User.get_cached_by_ap_id(activity.actor), + avatar = User.avatar_url(user) do + conn + |> render("status_interact.html", %{ + status_id: id, + nickname: user.nickname, + avatar: avatar, + error: false + }) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: "Could not find status" + }) + end + end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do @@ -74,6 +96,31 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do + get_ap_id = fn activity -> + object = Pleroma.Object.normalize(activity, fetch: false) + + case object do + %{data: %{"id" => ap_id}} -> {:ok, ap_id} + _ -> {:no_ap_id, nil} + end + end + + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), + %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id.(activity) do + conn + |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: "Something went wrong." + }) + end + end + def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do conn -- cgit v1.2.3 From a243a217a7006352542a22aca605e60fc80f9ff0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:12:00 -0500 Subject: Fix form item name in status_interact.html --- lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index bb3d0a0af..6354b409f 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -6,7 +6,7 @@ <%= @status_id %> <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> - <%= hidden_input f, :status, value: @status_id %> + <%= hidden_input f, :status_id, value: @status_id %> <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> <%= submit "Interact" %> <% end %> -- cgit v1.2.3 From 779457d9a4e6b3e5e8b7823119907c1eb24a3b87 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:41:46 -0500 Subject: Add GET endpoints for remote subscription forms There are two reasons for adding a GET endpoint: 0: Barely displaying the form does not change anything on the server. 1: It makes frontend development easier as they can now use a link, instead of a form, to allow remote users to interact with local ones. --- .../web/api_spec/operations/twitter_util_operation.ex | 10 ++++++++++ lib/pleroma/web/router.ex | 1 + .../web/twitter_api/controllers/util_controller.ex | 16 ++++++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 1cc90990f..29df03e34 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -405,6 +405,16 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } end + def show_subscribe_form_operation do + %Operation{ + tags: ["Accounts"], + summary: "Show remote subscribe form", + operationId: "UtilController.show_subscribe_form", + parameters: [], + responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} + } + end + defp delete_account_request do %Schema{ title: "AccountDeleteRequest", diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 842596e97..846ba8363 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -337,6 +337,7 @@ defmodule Pleroma.Web.Router do pipe_through(:pleroma_html) post("/main/ostatus", UtilController, :remote_subscribe) + get("/main/ostatus", UtilController, :show_subscribe_form) get("/ostatus_subscribe", RemoteFollowController, :follow) post("/ostatus_subscribe", RemoteFollowController, :do_follow) end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ee99aab3e..049329c38 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -17,8 +17,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe) - plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe) + plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe and action != :show_subscribe_form) + plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe when action == :show_subscribe_form) plug( OAuthScopesPlug, @@ -45,7 +45,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation - def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + def show_subscribe_form(conn, %{"nickname" => nick}) do with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do conn @@ -60,7 +60,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end - def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + def show_subscribe_form(conn, %{"status_id" => id}) do with %Activity{} = activity <- Activity.get_by_id(id), %User{} = user <- User.get_cached_by_ap_id(activity.actor), avatar = User.avatar_url(user) do @@ -81,6 +81,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + show_subscribe_form(conn, %{"nickname" => nick}) + end + + def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + show_subscribe_form(conn, %{"status_id" => id}) + end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do -- cgit v1.2.3 From b7c75db0f7f2c048d45fc387dfcf00073cbf8d62 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 16:58:08 -0500 Subject: Lint --- lib/pleroma/web/twitter_api/controllers/util_controller.ex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 049329c38..24b419c31 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -17,8 +17,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe and action != :show_subscribe_form) - plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe when action == :show_subscribe_form) + plug( + Pleroma.Web.ApiSpec.CastAndValidate + when action != :remote_subscribe and action != :show_subscribe_form + ) + + plug( + Pleroma.Web.Plugs.FederatingPlug + when action == :remote_subscribe + when action == :show_subscribe_form + ) plug( OAuthScopesPlug, -- cgit v1.2.3 From 1218adacc52f1235aedb1bb102d2e9385507efa4 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 28 Dec 2021 19:37:56 -0500 Subject: Display status link in remote interaction form --- .../twitter_api/util/status_interact.html.eex | 5 +---- .../web/twitter_api/controllers/util_controller.ex | 22 ++++++++++++---------- lib/pleroma/web/twitter_api/views/util_view.ex | 1 + 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index 6354b409f..695c5d64b 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -1,10 +1,7 @@ <%= if @error do %>

Error: <%= @error %>

<% else %> -

Interacting with <%= @nickname %>

-
- <%= @status_id %> -
+

Interacting with <%= @nickname %>'s <%= link("status", to: @status_link) %>

<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> <%= hidden_input f, :status_id, value: @status_id %> <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 24b419c31..2c3103185 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -70,10 +70,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do def show_subscribe_form(conn, %{"status_id" => id}) do with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id(activity), %User{} = user <- User.get_cached_by_ap_id(activity.actor), avatar = User.avatar_url(user) do conn |> render("status_interact.html", %{ + status_link: ap_id, status_id: id, nickname: user.nickname, avatar: avatar, @@ -113,18 +115,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do - get_ap_id = fn activity -> - object = Pleroma.Object.normalize(activity, fetch: false) - - case object do - %{data: %{"id" => ap_id}} -> {:ok, ap_id} - _ -> {:no_ap_id, nil} - end - end - with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %Activity{} = activity <- Activity.get_by_id(id), - {:ok, ap_id} <- get_ap_id.(activity) do + {:ok, ap_id} <- get_ap_id(activity) do conn |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) else @@ -146,6 +139,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + defp get_ap_id(activity) do + object = Pleroma.Object.normalize(activity, fetch: false) + + case object do + %{data: %{"id" => ap_id}} -> {:ok, ap_id} + _ -> {:no_ap_id, nil} + end + end + def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 69f243097..2365a396b 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view import Phoenix.HTML.Form + import Phoenix.HTML.Link alias Pleroma.Config alias Pleroma.Web.Endpoint alias Pleroma.Web.Gettext -- cgit v1.2.3 From 4ec9eeb3f8f3502841cd136ea7afe9298b477120 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 25 Mar 2022 22:05:28 -0400 Subject: Make remote interaction page translatable --- .../twitter_api/util/status_interact.html.eex | 8 +++---- .../web/twitter_api/controllers/util_controller.ex | 28 ++++++++++++++++++---- lib/pleroma/web/twitter_api/views/util_view.ex | 1 + 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex index 695c5d64b..d77174967 100644 --- a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -1,10 +1,10 @@ <%= if @error do %> -

Error: <%= @error %>

+

<%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %>

<% else %> -

Interacting with <%= @nickname %>'s <%= link("status", to: @status_link) %>

+

<%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %>

<%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> <%= hidden_input f, :status_id, value: @status_id %> - <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> - <%= submit "Interact" %> + <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %> + <%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %> <% end %> <% end %> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 2c3103185..d5a24ae6c 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -63,7 +63,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Could not find user" + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - user not found", + "Could not find user" + ) }) end end @@ -86,7 +91,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "status_interact.html", %{ status_id: id, avatar: nil, - error: "Could not find status" + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - status not found", + "Could not find status" + ) }) end end @@ -109,7 +119,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Something went wrong." + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - unknown error", + "Something went wrong." + ) }) end end @@ -125,7 +140,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "status_interact.html", %{ status_id: id, avatar: nil, - error: "Something went wrong." + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - unknown error", + "Something went wrong." + ) }) end end diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 2365a396b..31b7c0c0c 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view + import Phoenix.HTML import Phoenix.HTML.Form import Phoenix.HTML.Link alias Pleroma.Config -- cgit v1.2.3 From c59ee1f172628d37e1396e080876f0f3aebaf730 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 20 Aug 2022 21:19:31 -0400 Subject: Expose availability of GET /main/ostatus via instance --- lib/pleroma/web/mastodon_api/views/instance_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 62931bd41..dc44295e5 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -98,7 +98,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do end, if Config.get([:instance, :profile_directory]) do "profile_directory" - end + end, + "pleroma:get:main/ostatus" ] |> Enum.filter(& &1) end -- cgit v1.2.3 From 439c1baf25b19723bcbaac78b10d00181074e3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 23 Aug 2022 17:15:06 +0200 Subject: OAuthPlug: use user cache instead of joining As this plug is called on every request, this should reduce load on the database by not requiring to select on the users table every single time, and to instead use the by-ID user cache whenever possible. --- lib/pleroma/web/plugs/o_auth_plug.ex | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/o_auth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex index 0f74d626b..ba04ddb72 100644 --- a/lib/pleroma/web/plugs/o_auth_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_plug.ex @@ -47,15 +47,17 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do # @spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil defp fetch_user_and_token(token) do - query = + token_query = from(t in Token, - where: t.token == ^token, - join: user in assoc(t, :user), - preload: [user: user] + where: t.token == ^token ) - with %Token{user: user} = token_record <- Repo.one(query) do + with %Token{user_id: user_id} = token_record <- Repo.one(token_query), + false <- is_nil(user_id), + %User{} = user <- User.get_cached_by_id(user_id) do {:ok, user, token_record} + else + _ -> nil end end -- cgit v1.2.3 From 47e3a72b6ecff3fcc9eedf0dc23bffef5f8c9060 Mon Sep 17 00:00:00 2001 From: Ilja <672-ilja@users.noreply.git.pleroma.social> Date: Wed, 24 Aug 2022 15:24:07 +0000 Subject: fix flaky test_user_relationship_test.exs:81 --- lib/pleroma/user_relationship.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 5b3e593d3..fbecf3129 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -91,8 +91,9 @@ defmodule Pleroma.UserRelationship do expires_at: expires_at }) |> Repo.insert( - on_conflict: {:replace_all_except, [:id]}, - conflict_target: [:source_id, :relationship_type, :target_id] + on_conflict: {:replace_all_except, [:id, :inserted_at]}, + conflict_target: [:source_id, :relationship_type, :target_id], + returning: true ) end -- cgit v1.2.3 From 3afa1903ee202cc0acb4170bc06c491c15875145 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 27 Aug 2022 17:51:41 -0400 Subject: Do not stream out Create of ChatMessage --- lib/pleroma/activity/ir/topics.ex | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index 56c52e9d1..f058cc0c9 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -13,6 +13,14 @@ defmodule Pleroma.Activity.Ir.Topics do |> List.flatten() end + defp generate_topics(%{data: %{"type" => "ChatMessage"}}, %{data: %{"type" => "Delete"}}) do + ["user", "user:pleroma_chat"] + end + + defp generate_topics(%{data: %{"type" => "ChatMessage"}}, %{data: %{"type" => "Create"}}) do + [] + end + defp generate_topics(%{data: %{"type" => "Answer"}}, _) do [] end -- cgit v1.2.3 From f9b86c3c22c10d54a721cfe632f9c7455a8b2f9c Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 27 Aug 2022 19:34:56 -0400 Subject: Make local-only posts stream in local timeline --- lib/pleroma/activity/ir/topics.ex | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index f058cc0c9..fa4350797 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -39,6 +39,10 @@ defmodule Pleroma.Activity.Ir.Topics do end |> item_creation_tags(object, activity) + "local" -> + ["public:local"] + |> item_creation_tags(object, activity) + "direct" -> ["direct"] @@ -71,7 +75,18 @@ defmodule Pleroma.Activity.Ir.Topics do defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: [] - defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"] + defp attachment_topics(_object, %{local: true} = activity) do + case Visibility.get_visibility(activity) do + "public" -> + ["public:media", "public:local:media"] + + "local" -> + ["public:local:media"] + + _ -> + [] + end + end defp attachment_topics(_object, %{actor: actor}) when is_binary(actor), do: ["public:media", "public:remote:media:" <> URI.parse(actor).host] -- cgit v1.2.3 From ffd379456bb9e4f125ce5e2480be4d2819b88147 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 31 Aug 2022 15:57:06 -0400 Subject: Do not stream out Announces to public timelines --- lib/pleroma/activity/ir/topics.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index fa4350797..b9fcd6693 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Activity.Ir.Topics do ["user", "list"] ++ visibility_tags(object, activity) end - defp visibility_tags(object, activity) do + defp visibility_tags(object, %{data: %{"type" => "Create"}} = activity) do case Visibility.get_visibility(activity) do "public" -> if activity.local do @@ -51,6 +51,10 @@ defmodule Pleroma.Activity.Ir.Topics do end end + defp visibility_tags(_object, _activity) do + [] + end + defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do tags ++ remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) -- cgit v1.2.3 From 20a0dd6516e453cdedb5a7a2b7356c529eeacf84 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 31 Aug 2022 22:14:54 -0400 Subject: Exclude Announce instead of restricting to Create in visibility_tags --- lib/pleroma/activity/ir/topics.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index b9fcd6693..8249cbe27 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Activity.Ir.Topics do ["user", "list"] ++ visibility_tags(object, activity) end - defp visibility_tags(object, %{data: %{"type" => "Create"}} = activity) do + defp visibility_tags(object, %{data: %{"type" => type}} = activity) when type != "Announce" do case Visibility.get_visibility(activity) do "public" -> if activity.local do -- cgit v1.2.3 From 21ab7369cad6504be2f815aec888b38023d7a17a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 22:35:08 +0200 Subject: 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 --- lib/mix/tasks/pleroma/config.ex | 9 ++------- lib/pleroma/config/loader.ex | 15 ++------------- lib/pleroma/web/activity_pub/object_validator.ex | 3 +-- 3 files changed, 5 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 33d147d36..3a2ea44f8 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -304,13 +304,8 @@ defmodule Mix.Tasks.Pleroma.Config 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 015be3d8e..bd85eccab 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 f3e31c931..21442687c 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -204,8 +204,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do 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 -- cgit v1.2.3 From e124776d1448f9043d335dea9425578f37ad1a57 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 21:12:16 +0200 Subject: Elixir 1.14 formatting --- lib/mix/tasks/pleroma/user.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 50ffb7f27..929fa1717 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -112,9 +112,10 @@ defmodule Mix.Tasks.Pleroma.User 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}") -- cgit v1.2.3 From ec80a1e405c7b1d893c08ea99e824f2c13719c3a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 2 Sep 2022 22:35:08 +0200 Subject: Bump minimum Elixir version to 1.10 1.9 being end-of-life --- lib/pleroma/config/loader.ex | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 015be3d8e..bd85eccab 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) -- cgit v1.2.3 From 4477c6baff6ea3c17ceca5d9113960b5b78d5ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne?= Date: Tue, 23 Aug 2022 14:49:05 +0200 Subject: Metadata/Utils: use summary as description if set When generating OpenGraph and TwitterCard metadata for a post, the summary field will be used first if it is set to generate the post description. --- lib/pleroma/web/metadata/utils.ex | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 8052eaa44..15414a988 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -8,8 +8,8 @@ defmodule Pleroma.Web.Metadata.Utils do alias Pleroma.Formatter alias Pleroma.HTML - def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do - content + defp scrub_html_and_truncate_object_field(field, object) do + field # html content comes from DB already encoded, decode first and scrub after |> HtmlEntities.decode() |> String.replace(~r//, " ") @@ -19,6 +19,17 @@ defmodule Pleroma.Web.Metadata.Utils do |> Formatter.truncate() end + def scrub_html_and_truncate(%{data: %{"summary" => summary}} = object) + when is_binary(summary) and summary != "" do + summary + |> scrub_html_and_truncate_object_field(object) + end + + def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do + content + |> scrub_html_and_truncate_object_field(object) + end + def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do content |> scrub_html -- cgit v1.2.3 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: 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 ++++---- .../web/activity_pub/activity_pub_controller.ex | 52 ++++++---------------- lib/pleroma/web/activity_pub/views/user_view.ex | 2 - lib/pleroma/web/federator.ex | 6 +-- lib/pleroma/web/web_finger.ex | 4 -- 6 files changed, 25 insertions(+), 63 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index dbe6fd209..a7b8f48aa 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -59,9 +59,8 @@ defmodule Pleroma.Signature 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 a57295891..85d3382cb 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -711,6 +711,7 @@ defmodule Pleroma.User do |> 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User do follower_address: uri <> "/followers" } |> change + |> put_private_key() |> unique_constraint(:nickname) |> Repo.insert() |> set_cache() @@ -2351,17 +2359,6 @@ defmodule Pleroma.User 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 b8f63d69d..1357c379c 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do %{"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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController 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 52f6bb56d..f69fca075 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 @@ defmodule Pleroma.Web.ActivityPub.UserView 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 @@ defmodule Pleroma.Web.ActivityPub.UserView do 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 e7feefc07..3be71c1b6 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -61,10 +61,8 @@ defmodule Pleroma.Web.Federator 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 6cd9962ce..77ff40f46 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -63,8 +63,6 @@ defmodule Pleroma.Web.WebFinger 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 @@ defmodule Pleroma.Web.WebFinger do end def represent_user(user, "XML") do - {:ok, user} = User.ensure_keys_present(user) - aliases = user |> gather_aliases() -- cgit v1.2.3 From 50923f543826bb97efe8e01737ca854003ab934f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 8 Sep 2022 11:58:17 -0400 Subject: Fix User.get_or_fetch/1 with usernames starting with http --- lib/pleroma/user.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 85d3382cb..b422e5c1d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2126,7 +2126,8 @@ defmodule Pleroma.User do @doc "Gets or fetch a user by uri or nickname." @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()} - def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri) + def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri) + def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri) def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname) # wait a period of time and return newest version of the User structs -- cgit v1.2.3 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: 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 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fee3f1842..38accae5d 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -144,7 +144,7 @@ defmodule Pleroma.Object 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 @@ defmodule Pleroma.Object 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 f848aba3a..63caa915c 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 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do 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 -- cgit v1.2.3 From ea60c4e7097c69df2023f23f60451f69668394f8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 14 Sep 2022 20:24:04 -0400 Subject: Fix wrong relationship direction --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 50dd0e4c2..2b736e5a3 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountController 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) + render(conn, "relationship.json", user: followed, target: follower) else nil -> render_error(conn, :not_found, "Record not found") -- cgit v1.2.3 From 7f63b4c315653b4ed35afa326fc194feec21aea3 Mon Sep 17 00:00:00 2001 From: a1batross Date: Thu, 15 Sep 2022 22:38:35 +0200 Subject: 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 +++++ 1 file changed, 5 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index cd6f69f56..a7fb8fb83 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -94,6 +94,7 @@ defmodule Pleroma.User.Search 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 @@ defmodule Pleroma.User.Search 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, -- cgit v1.2.3 From 1958f23fe709fbd59f8eb09ed8749fffa2c91f23 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 30 Sep 2022 12:22:06 -0400 Subject: Fix deprecation warning for Gun timeout --- lib/pleroma/config/deprecation_warnings.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 599f1d3cf..b53b15d95 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -311,7 +311,7 @@ defmodule Pleroma.Config.DeprecationWarnings do warning_preface = """ !!!DEPRECATION WARNING!!! - Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later. + Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. The setting will not take effect until updated. """ updated_config = -- cgit v1.2.3 From 1b238a4fadd50811b1cce64812858c101e790c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 1 Oct 2022 23:28:02 +0200 Subject: Push.Impl: support edits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/push/impl.ex | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index daf3eeb9e..3c5f00764 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query - @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"] + @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"] @doc "Performs sending notifications for user subscriptions" @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} @@ -174,6 +174,15 @@ defmodule Pleroma.Web.Push.Impl do end end + def format_body( + %{activity: %{data: %{"type" => "Update"}}}, + actor, + _object, + _mastodon_type + ) do + "@#{actor.nickname} edited a status" + end + def format_title(activity, mastodon_type \\ nil) def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do @@ -187,6 +196,7 @@ defmodule Pleroma.Web.Push.Impl do "follow_request" -> "New Follow Request" "reblog" -> "New Repeat" "favourite" -> "New Favorite" + "update" -> "New Update" "pleroma:chat_mention" -> "New Chat Message" "pleroma:emoji_reaction" -> "New Reaction" type -> "New #{String.capitalize(type || "event")}" -- cgit v1.2.3 From 16b06160acbaec054736b18edf08d77e88a27aee Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 14 Oct 2022 18:32:13 +0200 Subject: CommonAPI: generate ModerationLog for all admin/moderator deletes As a side-effect it also changes the ChatMessage delete ID to an Activity.id rather than MessageReference.id Closes: https://git.pleroma.social/pleroma/pleroma/-/issues/2958 --- lib/pleroma/web/admin_api/controllers/chat_controller.ex | 7 ------- .../web/admin_api/controllers/status_controller.ex | 6 ------ lib/pleroma/web/common_api.ex | 16 ++++++++++++++++ 3 files changed, 16 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex index c3e9e12ce..298543fcf 100644 --- a/lib/pleroma/web/admin_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Chat.MessageReference - alias Pleroma.ModerationLog alias Pleroma.Pagination alias Pleroma.Web.AdminAPI alias Pleroma.Web.CommonAPI @@ -42,12 +41,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do ^chat_id <- to_string(cm_ref.chat_id), %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id), {:ok, _} <- CommonAPI.delete(activity_id, user) do - ModerationLog.insert_log(%{ - action: "chat_message_delete", - actor: user, - subject_id: message_id - }) - conn |> put_view(MessageReferenceView) |> render("show.json", chat_message_reference: cm_ref) diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex index c9a4bfde9..9a3d49b57 100644 --- a/lib/pleroma/web/admin_api/controllers/status_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex @@ -65,12 +65,6 @@ defmodule Pleroma.Web.AdminAPI.StatusController do def delete(%{assigns: %{user: user}} = conn, %{id: id}) do with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do - ModerationLog.insert_log(%{ - action: "status_delete", - actor: user, - subject_id: id - }) - json(conn, %{}) end end diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 89f5dd606..62ab6b69c 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.Conversation.Participation alias Pleroma.Formatter + alias Pleroma.ModerationLog alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -147,6 +148,21 @@ defmodule Pleroma.Web.CommonAPI do true <- User.superuser?(user) || user.ap_id == object.data["actor"], {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do + if User.superuser?(user) and user.ap_id != object.data["actor"] do + action = + if object.data["type"] == "ChatMessage" do + "chat_message_delete" + else + "status_delete" + end + + ModerationLog.insert_log(%{ + action: action, + actor: user, + subject_id: activity_id + }) + end + {:ok, delete} else {:find_activity, _} -> -- cgit v1.2.3 From 4121bca8957838f094ef134de4b54e492517e527 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 4 Mar 2021 13:06:12 +0300 Subject: expanding WebFinger --- lib/pleroma/http.ex | 9 ++++- lib/pleroma/web/activity_pub/activity_pub.ex | 57 +++++++++++++++------------- lib/pleroma/web/web_finger.ex | 45 ++++++++++++---------- 3 files changed, 64 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex index 2e82ceff2..d41061538 100644 --- a/lib/pleroma/http.ex +++ b/lib/pleroma/http.ex @@ -106,5 +106,12 @@ defmodule Pleroma.HTTP do [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool] end - defp adapter_middlewares(_), do: [] + defp adapter_middlewares(_) do + if Pleroma.Config.get(:env) == :test do + # Emulate redirects in test env, which are handled by adapters in other environments + [Tesla.Middleware.FollowRedirects] + else + [] + end + end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a5d7036d9..5099caef7 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1482,7 +1482,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() defp normalize_image(_), do: nil - defp object_to_user_data(data) do + defp object_to_user_data(data, additional) do fields = data |> Map.get("attachment", []) @@ -1514,15 +1514,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do public_key = if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do data["publicKey"]["publicKeyPem"] - else - nil end shared_inbox = if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do data["endpoints"]["sharedInbox"] - else - nil end birthday = @@ -1531,13 +1527,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, date} -> date {:error, _} -> nil end - else - nil end show_birthday = !!birthday - user_data = %{ + # if WebFinger request was already done, we probably have acct, otherwise + # we request WebFinger here + nickname = additional[:nickname_from_acct] || generate_nickname(data) + + %{ ap_id: data["id"], uri: get_actor_url(data["url"]), ap_enabled: true, @@ -1559,23 +1557,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do inbox: data["inbox"], shared_inbox: shared_inbox, accepts_chat_messages: accepts_chat_messages, - pinned_objects: pinned_objects, birthday: birthday, - show_birthday: show_birthday + show_birthday: show_birthday, + pinned_objects: pinned_objects, + nickname: nickname } + end - # nickname can be nil because of virtual actors - if data["preferredUsername"] do - Map.put( - user_data, - :nickname, - "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}" - ) + defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do + generated = "#{username}@#{URI.parse(data["id"]).host}" + + if Config.get([WebFinger, :update_nickname_on_user_fetch]) do + case WebFinger.finger(generated) do + {:ok, %{"subject" => "acct:" <> acct}} -> acct + _ -> generated + end else - Map.put(user_data, :nickname, nil) + generated end end + # nickname can be nil because of virtual actors + defp generate_nickname(_), do: nil + def fetch_follow_information_for_user(user) do with {:ok, following_data} <- Fetcher.fetch_and_contain_remote_object_from_id(user.following_address), @@ -1647,17 +1651,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp collection_private(_data), do: {:ok, true} - def user_data_from_user_object(data) do + def user_data_from_user_object(data, additional \\ []) do with {:ok, data} <- MRF.filter(data) do - {:ok, object_to_user_data(data)} + {:ok, object_to_user_data(data, additional)} else e -> {:error, e} end end - def fetch_and_prepare_user_from_ap_id(ap_id) do + def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id), - {:ok, data} <- user_data_from_user_object(data) do + {:ok, data} <- user_data_from_user_object(data, additional) do {:ok, maybe_update_follow_information(data)} else # If this has been deleted, only log a debug and not an error @@ -1735,13 +1739,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def make_user_from_ap_id(ap_id) do + def make_user_from_ap_id(ap_id, additional \\ []) do user = User.get_cached_by_ap_id(ap_id) if user && !User.ap_enabled?(user) do Transmogrifier.upgrade_user_from_ap_id(ap_id) else - with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do + with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end) if user do @@ -1761,8 +1765,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def make_user_from_nickname(nickname) do - with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do - make_user_from_ap_id(ap_id) + with {:ok, %{"ap_id" => ap_id, "subject" => "acct:" <> acct}} when not is_nil(ap_id) <- + WebFinger.finger(nickname) do + make_user_from_ap_id(ap_id, nickname_from_acct: acct) else _e -> {:error, "No AP id in WebFinger"} end diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 77ff40f46..3aed7e508 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -32,7 +32,13 @@ defmodule Pleroma.Web.WebFinger do def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do host = Pleroma.Web.Endpoint.host() - regex = ~r/(acct:)?(?[a-z0-9A-Z_\.-]+)@#{host}/ + + regex = + if webfinger_domain = Pleroma.Config.get([__MODULE__, :domain]) do + ~r/(acct:)?(?[a-z0-9A-Z_\.-]+)@(#{host}|#{webfinger_domain})/ + else + ~r/(acct:)?(?[a-z0-9A-Z_\.-]+)@#{host}/ + end with %{"username" => username} <- Regex.named_captures(regex, resource), %User{} = user <- User.get_cached_by_nickname(username) do @@ -63,8 +69,12 @@ defmodule Pleroma.Web.WebFinger do end def represent_user(user, "JSON") do + {:ok, user} = User.ensure_keys_present(user) + + domain = Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host() + %{ - "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", + "subject" => "acct:#{user.nickname}@#{domain}", "aliases" => gather_aliases(user), "links" => gather_links(user) } @@ -146,17 +156,15 @@ defmodule Pleroma.Web.WebFinger do end def find_lrdd_template(domain) do - with {:ok, %{status: status, body: body}} when status in 200..299 <- - HTTP.get("http://#{domain}/.well-known/host-meta") do + # WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1 + meta_url = "https://#{domain}/.well-known/host-meta" + + with {:ok, %{status: status, body: body}} when status in 200..299 <- HTTP.get(meta_url) do get_template_from_xml(body) else - _ -> - with {:ok, %{body: body, status: status}} when status in 200..299 <- - HTTP.get("https://#{domain}/.well-known/host-meta") do - get_template_from_xml(body) - else - e -> {:error, "Can't find LRDD template: #{inspect(e)}"} - end + error -> + Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}") + {:error, :lrdd_not_found} end end @@ -170,7 +178,7 @@ defmodule Pleroma.Web.WebFinger do end end - defp get_address_from_domain(_, _), do: nil + defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain} @spec finger(String.t()) :: {:ok, map()} | {:error, any()} def finger(account) do @@ -187,13 +195,11 @@ defmodule Pleroma.Web.WebFinger do encoded_account = URI.encode("acct:#{account}") with address when is_binary(address) <- get_address_from_domain(domain, encoded_account), - response <- + {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- HTTP.get( address, [{"accept", "application/xrd+xml,application/jrd+json"}] - ), - {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- - response do + ) do case List.keyfind(headers, "content-type", 0) do {_, content_type} -> case Plug.Conn.Utils.media_type(content_type) do @@ -211,10 +217,9 @@ defmodule Pleroma.Web.WebFinger do {:error, {:content_type, nil}} end else - e -> - Logger.debug(fn -> "Couldn't finger #{account}" end) - Logger.debug(fn -> inspect(e) end) - {:error, e} + error -> + Logger.debug("Couldn't finger #{account}: #{inspect(error)}") + error end end end -- cgit v1.2.3 From 5a9ea98baf1d64a1ba61b0f88b2b0e0334bd5ca3 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 4 Mar 2021 19:14:00 +0300 Subject: XML WebFinger user representation correct domain --- lib/pleroma/web/web_finger.ex | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 3aed7e508..967935e5e 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -71,10 +71,8 @@ defmodule Pleroma.Web.WebFinger do def represent_user(user, "JSON") do {:ok, user} = User.ensure_keys_present(user) - domain = Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host() - %{ - "subject" => "acct:#{user.nickname}@#{domain}", + "subject" => "acct:#{user.nickname}@#{domain()}", "aliases" => gather_aliases(user), "links" => gather_links(user) } @@ -94,12 +92,16 @@ defmodule Pleroma.Web.WebFinger do :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ - {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"} + {:Subject, "acct:#{user.nickname}@#{domain()}"} ] ++ aliases ++ links } |> XmlBuilder.to_doc() end + defp domain do + Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host() + end + defp webfinger_from_xml(body) do with {:ok, doc} <- XML.parse_document(body) do subject = XML.string_from_xpath("//Subject", doc) -- cgit v1.2.3 From 8407e26b0c1ec315fe8864948c78657f29f370c7 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sun, 21 Mar 2021 13:41:20 +0300 Subject: rebase fix --- lib/pleroma/web/web_finger.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 967935e5e..f95dc2458 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -69,8 +69,6 @@ defmodule Pleroma.Web.WebFinger do end def represent_user(user, "JSON") do - {:ok, user} = User.ensure_keys_present(user) - %{ "subject" => "acct:#{user.nickname}@#{domain()}", "aliases" => gather_aliases(user), -- cgit v1.2.3 From 7c64f705f6ee0364f1e016004bb0baf79d88dae2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 2 Nov 2022 21:56:43 -0400 Subject: Update to Phoenix 1.6 and chase dependencies Also bump minimum Elixir to 1.11 --- lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex | 2 +- lib/pleroma/web/templates/feed/feed/tag.atom.eex | 2 +- lib/pleroma/web/templates/feed/feed/tag.rss.eex | 2 +- lib/pleroma/web/templates/feed/feed/user.atom.eex | 4 ++-- lib/pleroma/web/templates/feed/feed/user.rss.eex | 4 ++-- lib/pleroma/web/templates/layout/email.html.eex | 2 +- lib/pleroma/web/templates/layout/embed.html.eex | 2 +- lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex | 2 +- lib/pleroma/web/templates/o_auth/o_auth/show.html.eex | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index aa3035bca..7e2e587e1 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -2,7 +2,7 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post - <%= render @view_module, "_tag_author.atom", assigns %> + <%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %> <%= @data["id"] %> <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index 6d497e84c..8c551feaf 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -17,6 +17,6 @@ <%= most_recent_update(@activities) %> <%= for activity <- @activities do %> - <%= render @view_module, "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> + <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex index edcc3e436..86466d367 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -9,7 +9,7 @@ <%= feed_logo() %> 2b90d9 <%= for activity <- @activities do %> - <%= render @view_module, "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex index 5c1f0ecbc..97a7535ab 100644 --- a/lib/pleroma/web/templates/feed/feed/user.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex @@ -12,13 +12,13 @@ <%= logo(@user) %> - <%= render @view_module, "_author.atom", assigns %> + <%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %> <%= if last_activity(@activities) do %> <% end %> <%= for activity <- @activities do %> - <%= render @view_module, "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex index 6b842a085..a9fee244c 100644 --- a/lib/pleroma/web/templates/feed/feed/user.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex @@ -7,14 +7,14 @@ <%= logo(@user) %> <%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %> - <%= render @view_module, "_author.rss", assigns %> + <%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %> <%= if last_activity(@activities) do %> <%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %> <% end %> <%= for activity <- @activities do %> - <%= render @view_module, "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %> <% end %> diff --git a/lib/pleroma/web/templates/layout/email.html.eex b/lib/pleroma/web/templates/layout/email.html.eex index 087aa4fc0..5858e48b4 100644 --- a/lib/pleroma/web/templates/layout/email.html.eex +++ b/lib/pleroma/web/templates/layout/email.html.eex @@ -5,6 +5,6 @@ <%= @email.subject %> - <%= render @view_module, @view_template, assigns %> + <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %> diff --git a/lib/pleroma/web/templates/layout/embed.html.eex b/lib/pleroma/web/templates/layout/embed.html.eex index 8b905f070..1197288e5 100644 --- a/lib/pleroma/web/templates/layout/embed.html.eex +++ b/lib/pleroma/web/templates/layout/embed.html.eex @@ -10,6 +10,6 @@ - <%= render @view_module, @view_template, assigns %> + <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex index 8b894cd58..98904ad64 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex @@ -2,7 +2,7 @@ <%= form_for @conn, Routes.o_auth_path(@conn, :prepare_request), [as: "authorization", method: "get"], fn f -> %>
- <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> + <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %>
<%= hidden_input f, :client_id, value: @client_id %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index a2f41618e..b3654f3eb 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -21,7 +21,7 @@
<%= if @app do %>

<%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application %{client_name} is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %>

- <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> + <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %> <% end %> <%= if @user do %> @@ -63,5 +63,5 @@ <% end %> <%= if Pleroma.Config.oauth_consumer_enabled?() do %> - <%= render @view_module, Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %> + <%= render Phoenix.Controller.view_module(@conn), Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %> <% end %> -- cgit v1.2.3 From bdedc41cbc60cc699e44ff323b945c276af32122 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 4 Nov 2022 09:43:13 +0100 Subject: Fix typo in CSP Report-To header name The header name was Report-To, not Reply-To. In any case, that's now being changed to the Reporting-Endpoints HTTP Response Header. https://w3c.github.io/reporting/#header https://github.com/w3c/reporting/issues/177 CanIUse says the Report-To header is still supported by current Chrome and friends. https://caniuse.com/mdn-http_headers_report-to It doesn't have any data for the Reporting-Endpoints HTTP header, but this article says Chrome 96 supports it. https://web.dev/reporting-api/ (Even though that's come out one year ago, that's not compatible with Network Error Logging which's still using the Report-To version of the API) Signed-off-by: Thomas Citharel --- lib/pleroma/web/plugs/http_security_plug.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index b89948cec..cd1bae235 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -68,7 +68,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do ] } - [{"reply-to", Jason.encode!(report_group)} | headers] + [{"report-to", Jason.encode!(report_group)} | headers] else headers end -- cgit v1.2.3 From 648e012022f1b0a65ed21f9576b479a12daeba6c Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 7 Nov 2022 14:56:59 +0100 Subject: ObjectAgePolicy: Make strip_followers behavior for followers-only explicit --- lib/pleroma/web/activity_pub/mrf/object_age_policy.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 0e9d25a0a..df1a6dcbb 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -131,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do type: {:list, :atom}, description: "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> - "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <> + "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct message; " <> "`:reject` rejects the message entirely", suggestions: [:delist, :strip_followers, :reject] } -- cgit v1.2.3 From 6f047cc308352cb3437f95e31e73487bba194abe Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 9 Nov 2022 22:36:42 -0500 Subject: Do not strip reported statuses when configured not to --- lib/pleroma/web/activity_pub/utils.ex | 23 +++++++++++------------ lib/pleroma/web/admin_api/report.ex | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index d3b7d804f..57a2f53c4 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -748,22 +748,21 @@ defmodule Pleroma.Web.ActivityPub.Utils do ActivityPub.fetch_activities([], params, :offset) end - def update_report_state(%Activity{} = activity, state) - when state in @strip_status_report_states do - {:ok, stripped_activity} = strip_report_status_data(activity) + defp maybe_strip_report_status(data, state) do + with true <- Config.get([:instance, :report_strip_status]), + true <- state in @strip_status_report_states, + {:ok, stripped_activity} = strip_report_status_data(%Activity{data: data}) do + data |> Map.put("object", stripped_activity.data["object"]) + else + _ -> data + end + end + def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do new_data = activity.data |> Map.put("state", state) - |> Map.put("object", stripped_activity.data["object"]) - - activity - |> Changeset.change(data: new_data) - |> Repo.update() - end - - def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do - new_data = Map.put(activity.data, "state", state) + |> maybe_strip_report_status(state) activity |> Changeset.change(data: new_data) diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex index 8d1abfa56..f377e1804 100644 --- a/lib/pleroma/web/admin_api/report.ex +++ b/lib/pleroma/web/admin_api/report.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.AdminAPI.Report do alias Pleroma.Activity + alias Pleroma.Object alias Pleroma.User def extract_report_info( @@ -16,10 +17,38 @@ defmodule Pleroma.Web.AdminAPI.Report do status_ap_ids |> Enum.reject(&is_nil(&1)) |> Enum.map(fn - act when is_map(act) -> Activity.get_by_ap_id_with_object(act["id"]) - act when is_binary(act) -> Activity.get_by_ap_id_with_object(act) + act when is_map(act) -> + Activity.get_by_ap_id_with_object(act["id"]) || make_fake_activity(act, user) + + act when is_binary(act) -> + Activity.get_by_ap_id_with_object(act) end) %{report: report, user: user, account: account, statuses: statuses} end + + defp make_fake_activity(act, user) do + %Activity{ + id: "pleroma:fake", + data: %{ + "actor" => user.ap_id, + "type" => "Create", + "to" => [], + "cc" => [], + "object" => act["id"], + "published" => act["published"] + }, + recipients: [user.ap_id], + object: %Object{ + data: %{ + "actor" => user.ap_id, + "type" => "Note", + "content" => act["content"], + "published" => act["published"], + "to" => [], + "cc" => [] + } + } + } + end end -- cgit v1.2.3 From 717c5901f893527b059201d1ce7899060a18a1a5 Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 9 Nov 2022 23:02:27 -0500 Subject: Render a generated reported activity properly --- lib/pleroma/web/admin_api/report.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex index f377e1804..6856bfcb3 100644 --- a/lib/pleroma/web/admin_api/report.ex +++ b/lib/pleroma/web/admin_api/report.ex @@ -36,7 +36,9 @@ defmodule Pleroma.Web.AdminAPI.Report do "to" => [], "cc" => [], "object" => act["id"], - "published" => act["published"] + "published" => act["published"], + "id" => act["id"], + "context" => "pleroma:fake" }, recipients: [user.ap_id], object: %Object{ @@ -46,7 +48,9 @@ defmodule Pleroma.Web.AdminAPI.Report do "content" => act["content"], "published" => act["published"], "to" => [], - "cc" => [] + "cc" => [], + "id" => act["id"], + "context" => "pleroma:fake" } } } -- cgit v1.2.3 From 6b87b3f2eae62a7d6e20681468c367489a47f0a3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Nov 2022 11:39:43 -0500 Subject: Remove Quack logging backend --- lib/pleroma/config/transfer_task.ex | 7 +------ lib/pleroma/config_db.ex | 3 +-- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 4199630af..44a984019 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -47,7 +47,7 @@ defmodule Pleroma.Config.TransferTask do {logger, other} = (Repo.all(ConfigDB) ++ deleted_settings) |> Enum.map(&merge_with_default/1) - |> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end) + |> Enum.split_with(fn {group, _, _, _} -> group in [:logger] end) logger |> Enum.sort() @@ -104,11 +104,6 @@ defmodule Pleroma.Config.TransferTask do end # change logger configuration in runtime, without restart - defp configure({:quack, key, _, merged}) do - Logger.configure_backend(Quack.Logger, [{key, merged}]) - :ok = update_env(:quack, key, merged) - end - defp configure({_, :backends, _, merged}) do # removing current backends Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1) diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index 6befbbe19..846cede04 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -163,7 +163,6 @@ defmodule Pleroma.ConfigDB do defp only_full_update?(%ConfigDB{group: group, key: key}) do full_key_update = [ {:pleroma, :ecto_repos}, - {:quack, :meta}, {:mime, :types}, {:cors_plug, [:max_age, :methods, :expose, :headers]}, {:swarm, :node_blacklist}, @@ -386,7 +385,7 @@ defmodule Pleroma.ConfigDB do @spec module_name?(String.t()) :: boolean() def module_name?(string) do - Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or + Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Ueberauth|Swoosh)\./, string) or string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"] end end -- cgit v1.2.3 From 8be7f87e1f1513998584258aee80231e3347b31f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 11 Nov 2022 13:42:29 -0500 Subject: Define sane Oban Worker timeouts --- lib/pleroma/workers/attachments_cleanup_worker.ex | 3 +++ lib/pleroma/workers/background_worker.ex | 3 +++ lib/pleroma/workers/backup_worker.ex | 4 ++++ lib/pleroma/workers/mailer_worker.ex | 3 +++ lib/pleroma/workers/mute_expire_worker.ex | 3 +++ lib/pleroma/workers/poll_worker.ex | 3 +++ lib/pleroma/workers/publisher_worker.ex | 3 +++ lib/pleroma/workers/purge_expired_activity.ex | 3 +++ lib/pleroma/workers/purge_expired_filter.ex | 3 +++ lib/pleroma/workers/purge_expired_token.ex | 3 +++ lib/pleroma/workers/receiver_worker.ex | 3 +++ lib/pleroma/workers/remote_fetcher_worker.ex | 3 +++ lib/pleroma/workers/scheduled_activity_worker.ex | 3 +++ lib/pleroma/workers/transmogrifier_worker.ex | 3 +++ lib/pleroma/workers/web_pusher_worker.ex | 3 +++ 15 files changed, 46 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index 0a397eae0..4c1764053 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -31,6 +31,9 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do def perform(%Job{args: %{"op" => "cleanup_attachments", "object" => _object}}), do: {:ok, :skip} + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(900) + defp do_clean({object_ids, attachment_urls}) do uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 91440cbe6..3805293bc 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -43,4 +43,7 @@ defmodule Pleroma.Workers.BackgroundWorker do def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do Instance.perform(:delete_instance, host) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex index 7657fa9ce..12ee70f00 100644 --- a/lib/pleroma/workers/backup_worker.ex +++ b/lib/pleroma/workers/backup_worker.ex @@ -30,6 +30,7 @@ defmodule Pleroma.Workers.BackupWorker do |> Oban.insert() end + @impl Oban.Worker def perform(%Job{ args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id} }) do @@ -49,6 +50,9 @@ defmodule Pleroma.Workers.BackupWorker do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(900) + defp has_email?(user) do not is_nil(user.email) and user.email != "" end diff --git a/lib/pleroma/workers/mailer_worker.ex b/lib/pleroma/workers/mailer_worker.ex index 81764ba72..940716558 100644 --- a/lib/pleroma/workers/mailer_worker.ex +++ b/lib/pleroma/workers/mailer_worker.ex @@ -12,4 +12,7 @@ defmodule Pleroma.Workers.MailerWorker do |> :erlang.binary_to_term() |> Pleroma.Emails.Mailer.deliver(config) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index a7841d917..8ce458d48 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -17,4 +17,7 @@ defmodule Pleroma.Workers.MuteExpireWorker do Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id) :ok end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 4c7eab5c1..022d026f8 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -19,6 +19,9 @@ defmodule Pleroma.Workers.PollWorker do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + defp find_poll_activity(activity_id) do with nil <- Activity.get_by_id(activity_id) do {:error, :poll_activity_not_found} diff --git a/lib/pleroma/workers/publisher_worker.ex b/lib/pleroma/workers/publisher_worker.ex index 528a06bb3..598ae3779 100644 --- a/lib/pleroma/workers/publisher_worker.ex +++ b/lib/pleroma/workers/publisher_worker.ex @@ -22,4 +22,7 @@ defmodule Pleroma.Workers.PublisherWorker do params = Map.new(params, fn {k, v} -> {String.to_atom(k), v} end) Federator.perform(:publish_one, String.to_atom(module_name), params) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(10) end diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index 0545d3ece..e554684fe 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -35,6 +35,9 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + defp enabled? do with false <- Pleroma.Config.get([__MODULE__, :enabled], false) do {:error, :expired_activities_disabled} diff --git a/lib/pleroma/workers/purge_expired_filter.ex b/lib/pleroma/workers/purge_expired_filter.ex index 933ecb3f6..9114aeb7f 100644 --- a/lib/pleroma/workers/purge_expired_filter.ex +++ b/lib/pleroma/workers/purge_expired_filter.ex @@ -31,6 +31,9 @@ defmodule Pleroma.Workers.PurgeExpiredFilter do |> Repo.delete() end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + @spec get_expiration(pos_integer()) :: Job.t() | nil def get_expiration(id) do from(j in Job, diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex index 1d322b6b6..2ccd9e80b 100644 --- a/lib/pleroma/workers/purge_expired_token.ex +++ b/lib/pleroma/workers/purge_expired_token.ex @@ -26,4 +26,7 @@ defmodule Pleroma.Workers.PurgeExpiredToken do |> Pleroma.Repo.get(id) |> Pleroma.Repo.delete() end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index c41b44e14..4f513b907 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -17,4 +17,7 @@ defmodule Pleroma.Workers.ReceiverWorker do e -> e end end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index c3158bbbe..d2a77aa17 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -11,4 +11,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do {:ok, _object} = Fetcher.fetch_object_from_id(id, depth: args["depth"]) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(10) end diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index 9a17330b6..4df84d00f 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -37,6 +37,9 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + defp find_scheduled_activity(id) do with nil <- Repo.get(ScheduledActivity, id) do {:error, :scheduled_activity_not_found} diff --git a/lib/pleroma/workers/transmogrifier_worker.ex b/lib/pleroma/workers/transmogrifier_worker.ex index ed319c585..1f3f5385e 100644 --- a/lib/pleroma/workers/transmogrifier_worker.ex +++ b/lib/pleroma/workers/transmogrifier_worker.ex @@ -12,4 +12,7 @@ defmodule Pleroma.Workers.TransmogrifierWorker do user = User.get_cached_by_id(user_id) Pleroma.Web.ActivityPub.Transmogrifier.perform(:user_upgrade, user) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex index 6447a5edc..67e84b0c9 100644 --- a/lib/pleroma/workers/web_pusher_worker.ex +++ b/lib/pleroma/workers/web_pusher_worker.ex @@ -17,4 +17,7 @@ defmodule Pleroma.Workers.WebPusherWorker do Pleroma.Web.Push.Impl.perform(notification) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end -- cgit v1.2.3 From 4d321be05c0aa34921f96f38c17dd611691f3c44 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Mon, 10 Oct 2022 15:55:58 +0100 Subject: Extract deactivated users query to a join --- lib/pleroma/activity.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index ebfd4ed45..3556aaf9e 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -361,9 +361,11 @@ defmodule Pleroma.Activity do end def restrict_deactivated_users(query) do - deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id) - - from(activity in query, where: activity.actor not in subquery(deactivated_users_query)) + query + |> join(:inner, [activity], user in User, + as: :user, + on: activity.actor == user.ap_id and user.is_active == true + ) end defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search -- cgit v1.2.3 From 749445dd50ba9376779c902584da3b55be7270bb Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 12 Nov 2022 17:52:37 -0500 Subject: Fix reports which do not have a user The check for deactivated users was being applied to report activities. --- lib/pleroma/web/activity_pub/activity_pub.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5099caef7..ad9eb2505 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1239,6 +1239,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp exclude_invisible_actors(query, %{type: "Flag"}), do: query defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query defp exclude_invisible_actors(query, _opts) do @@ -1377,7 +1378,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_instance(opts) |> restrict_announce_object_actor(opts) |> restrict_filtered(opts) - |> Activity.restrict_deactivated_users() + |> maybe_restrict_deactivated_users(opts) |> exclude_poll_votes(opts) |> exclude_chat_messages(opts) |> exclude_invisible_actors(opts) @@ -1789,4 +1790,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_visibility(%{visibility: "direct"}) |> order_by([activity], asc: activity.id) end + + defp maybe_restrict_deactivated_users(activity, %{type: "Flag"}), do: activity + + defp maybe_restrict_deactivated_users(activity, _opts), + do: Activity.restrict_deactivated_users(activity) end -- cgit v1.2.3 From edaf0a05f8d3b9476d868e153fbca029e74b4bd3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 12 Nov 2022 18:05:58 -0500 Subject: Add same optimized join for excluding invisible users --- lib/pleroma/web/activity_pub/activity_pub.ex | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index ad9eb2505..fa251394b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1243,12 +1243,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query defp exclude_invisible_actors(query, _opts) do - invisible_ap_ids = - User.Query.build(%{invisible: true, select: [:ap_id]}) - |> Repo.all() - |> Enum.map(fn %{ap_id: ap_id} -> ap_id end) - - from([activity] in query, where: activity.actor not in ^invisible_ap_ids) + query + |> join(:inner, [activity], u in User, + as: :u, + on: activity.actor == u.ap_id and u.invisible == false + ) end defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do -- cgit v1.2.3 From b2713357b9410fc43478de4fb271b2920d618956 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 11 Nov 2022 12:02:26 +0100 Subject: Object.Fetcher: Set reachable on successful fetch --- lib/pleroma/object/fetcher.ex | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index d81fdcf24..a9a9eeeed 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Object.Fetcher do alias Pleroma.HTTP + alias Pleroma.Instances alias Pleroma.Maps alias Pleroma.Object alias Pleroma.Object.Containment @@ -234,6 +235,10 @@ defmodule Pleroma.Object.Fetcher do {:ok, body} <- get_object(id), {:ok, data} <- safe_json_decode(body), :ok <- Containment.contain_origin_from_id(id, data) do + if not Instances.reachable?(id) do + Instances.set_reachable(id) + end + {:ok, data} else {:scheme, _} -> -- cgit v1.2.3 From 47b9847edd74c394e2bdfcb95a42f858329c9ef1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 13 Nov 2022 12:25:52 -0500 Subject: Deletes do not generate notifications of any kind, so skip trying --- lib/pleroma/web/activity_pub/side_effects.ex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 5eefd2824..b2e15e1a0 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -324,7 +324,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end if result == :ok do - Notification.create_notifications(object) {:ok, object, meta} else {:error, result} -- cgit v1.2.3 From 2e0089dd5c27c86488d2c68170d80d02c24135f3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 13 Nov 2022 13:33:27 -0500 Subject: Alter priority of Delete activities to be lowest This will prevent a user with a large number of posts from negatively affecting performance of the outgoing federation queue if they delete their account. --- lib/pleroma/web/activity_pub/side_effects.ex | 1 - lib/pleroma/web/federator.ex | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index b2e15e1a0..a2152b945 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -282,7 +282,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # Tasks this handles: # - Delete and unpins the create activity # - Replace object with Tombstone - # - Set up notification # - Reduce the user note count # - Reduce the reply count # - Stream out the activity diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 3be71c1b6..318b6cb11 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -47,10 +47,15 @@ defmodule Pleroma.Web.Federator do end @impl true - def publish(activity) do - PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}) + def publish(%Pleroma.Activity{data: %{"type" => type}} = activity) do + PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}, + priority: publish_priority(type) + ) end + defp publish_priority("Delete"), do: 3 + defp publish_priority(_), do: 0 + # Job Worker Callbacks @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()} -- cgit v1.2.3 From db76ea578a550a4cbc0298d428b9c57ba605b276 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 12 Apr 2021 00:38:25 +0300 Subject: try to fix ruffle on chrome --- lib/pleroma/web/plugs/http_security_plug.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index cd1bae235..3ee48062e 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -117,7 +117,9 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do if Config.get(:env) == :dev do "script-src 'self' 'unsafe-eval'" else - "script-src 'self'" + # TODO right now unsafe-eval is needed for WASM to load in chrome + # see: https://github.com/WebAssembly/content-security-policy/issues/7 + "script-src 'self' 'unsafe-eval'" end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] -- cgit v1.2.3 From 79bd363a68cce0600c93eaa4ac08782333c3e8bb Mon Sep 17 00:00:00 2001 From: HJ <30-hj@users.noreply.git.pleroma.social> Date: Wed, 16 Nov 2022 21:27:04 +0000 Subject: Update lib/pleroma/web/plugs/http_security_plug.ex --- lib/pleroma/web/plugs/http_security_plug.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index 3ee48062e..7a987a30b 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -119,7 +119,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do else # TODO right now unsafe-eval is needed for WASM to load in chrome # see: https://github.com/WebAssembly/content-security-policy/issues/7 - "script-src 'self' 'unsafe-eval'" + "script-src 'self' 'wasm-unsafe-eval'" end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] -- cgit v1.2.3 From a31d3589ed8c91cceece7dbdf362c9bfe69e0115 Mon Sep 17 00:00:00 2001 From: HJ <30-hj@users.noreply.git.pleroma.social> Date: Wed, 16 Nov 2022 21:28:35 +0000 Subject: Update http_security_plug.ex --- lib/pleroma/web/plugs/http_security_plug.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index 7a987a30b..34895c8d5 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -117,8 +117,6 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do if Config.get(:env) == :dev do "script-src 'self' 'unsafe-eval'" else - # TODO right now unsafe-eval is needed for WASM to load in chrome - # see: https://github.com/WebAssembly/content-security-policy/issues/7 "script-src 'self' 'wasm-unsafe-eval'" end -- cgit v1.2.3 From a69e9ae2ef943f57faf803441c25aee09a86f9ca Mon Sep 17 00:00:00 2001 From: tusooa Date: Sat, 19 Nov 2022 23:51:43 -0500 Subject: Flag an Object, not an Activity --- lib/pleroma/web/activity_pub/utils.ex | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 57a2f53c4..dacacb83b 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -695,8 +695,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do Enum.map(statuses || [], &build_flag_object/1) end - defp build_flag_object(%Activity{data: %{"id" => id}, object: %{data: data}}) do - activity_actor = User.get_by_ap_id(data["actor"]) + defp build_flag_object(%Object{data: data}) do + actor = User.get_by_ap_id(data["actor"]) + id = data["id"] %{ "type" => "Note", @@ -706,7 +707,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do "actor" => AccountView.render( "show.json", - %{user: activity_actor, skip_visibility_check: true} + %{user: actor, skip_visibility_check: true} ) } end @@ -720,12 +721,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do end case Activity.get_by_ap_id_with_object(id) do - %Activity{} = activity -> - build_flag_object(activity) + %Activity{object: object} = _ -> + build_flag_object(object) nil -> - if activity = Activity.get_by_object_ap_id_with_object(id) do - build_flag_object(activity) + if %Object{} = object = Object.get_by_ap_id(id) do + build_flag_object(object) else %{"id" => id, "deleted" => true} end -- cgit v1.2.3 From 0e0c316c76527fb5ac3a28f03628e62799e8694b Mon Sep 17 00:00:00 2001 From: tusooa Date: Sun, 20 Nov 2022 00:35:52 -0500 Subject: Fix report api --- lib/pleroma/web/activity_pub/utils.ex | 5 +++++ lib/pleroma/web/admin_api/report.ex | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index dacacb83b..22057e703 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -695,6 +695,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do Enum.map(statuses || [], &build_flag_object/1) end + defp build_flag_object(%Activity{} = activity) do + object = Object.normalize(activity, fetch: false) + build_flag_object(object) + end + defp build_flag_object(%Object{data: data}) do actor = User.get_by_ap_id(data["actor"]) id = data["id"] diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex index 6856bfcb3..c79bee27e 100644 --- a/lib/pleroma/web/admin_api/report.ex +++ b/lib/pleroma/web/admin_api/report.ex @@ -18,10 +18,12 @@ defmodule Pleroma.Web.AdminAPI.Report do |> Enum.reject(&is_nil(&1)) |> Enum.map(fn act when is_map(act) -> - Activity.get_by_ap_id_with_object(act["id"]) || make_fake_activity(act, user) + Activity.get_create_by_object_ap_id_with_object(act["id"]) || + Activity.get_by_ap_id_with_object(act["id"]) || make_fake_activity(act, user) act when is_binary(act) -> - Activity.get_by_ap_id_with_object(act) + Activity.get_create_by_object_ap_id_with_object(act) || + Activity.get_by_ap_id_with_object(act) end) %{report: report, user: user, account: account, statuses: statuses} -- cgit v1.2.3 From 9d99e76a3ae7c1af8b6b03bf88534f3959b62544 Mon Sep 17 00:00:00 2001 From: tusooa Date: Sun, 20 Nov 2022 00:57:04 -0500 Subject: Fix unit tests --- lib/pleroma/web/activity_pub/utils.ex | 44 ++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 22057e703..b898d6fe8 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -697,24 +697,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do defp build_flag_object(%Activity{} = activity) do object = Object.normalize(activity, fetch: false) - build_flag_object(object) - end - defp build_flag_object(%Object{data: data}) do - actor = User.get_by_ap_id(data["actor"]) - id = data["id"] + # Do not allow people to report Creates. Instead, report the Object that is Created. + if activity.data["type"] != "Create" do + build_flag_object_with_actor_and_id( + object, + User.get_by_ap_id(activity.data["actor"]), + activity.data["id"] + ) + else + build_flag_object(object) + end + end - %{ - "type" => "Note", - "id" => id, - "content" => data["content"], - "published" => data["published"], - "actor" => - AccountView.render( - "show.json", - %{user: actor, skip_visibility_check: true} - ) - } + defp build_flag_object(%Object{} = object) do + actor = User.get_by_ap_id(object.data["actor"]) + build_flag_object_with_actor_and_id(object, actor, object.data["id"]) end defp build_flag_object(act) when is_map(act) or is_binary(act) do @@ -740,6 +738,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do defp build_flag_object(_), do: [] + defp build_flag_object_with_actor_and_id(%Object{data: data}, actor, id) do + %{ + "type" => "Note", + "id" => id, + "content" => data["content"], + "published" => data["published"], + "actor" => + AccountView.render( + "show.json", + %{user: actor, skip_visibility_check: true} + ) + } + end + #### Report-related helpers def get_reports(params, page, page_size) do params = -- cgit v1.2.3 From 0f88c2bca437b7d2fa6d6320aae8b8bbe4e5d6c4 Mon Sep 17 00:00:00 2001 From: ave Date: Mon, 28 Nov 2022 00:13:34 +0000 Subject: Change follow_operation schema to use type BooleanLike --- lib/pleroma/web/api_spec/operations/account_operation.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index aed59293c..012cbdc79 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -223,12 +223,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do type: :object, properties: %{ reblogs: %Schema{ - type: :boolean, + allOf: [BooleanLike], description: "Receive this account's reblogs in home timeline? Defaults to true.", default: true }, notify: %Schema{ - type: :boolean, + allOf: [BooleanLike], description: "Receive notifications for all statuses posted by the account? Defaults to false.", default: false -- cgit v1.2.3 From 452595baeda8327b862d03e450ac004679efe86e Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 11 Dec 2022 22:54:47 +0100 Subject: Uploading an avatar media exceeding max size returns a 413 Until now it was returning a 500 because the upload plug were going through the changeset and ending in the JSON encoder, which raised because struct has to @derive the encoder. --- lib/pleroma/user.ex | 8 +++++++- lib/pleroma/web/api_spec/operations/account_operation.ex | 3 ++- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index b422e5c1d..17f6b9a0d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -611,7 +611,13 @@ defmodule Pleroma.User do {:ok, new_value} <- value_function.(value) do put_change(changeset, map_field, new_value) else - _ -> changeset + {:error, :file_too_large} -> + Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value -> + [{map_field, "file is too large"}] + end) + + _ -> + changeset end end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 012cbdc79..aabe988f7 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -64,7 +64,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do requestBody: request_body("Parameters", update_credentials_request(), required: true), responses: %{ 200 => Operation.response("Account", "application/json", Account), - 403 => Operation.response("Error", "application/json", ApiError) + 403 => Operation.response("Error", "application/json", ApiError), + 413 => Operation.response("Error", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 7c24c35d2..656a861cd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -254,7 +254,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do with_pleroma_settings: true ) else - _e -> render_error(conn, :forbidden, "Invalid request") + {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + + _e -> + render_error(conn, :forbidden, "Invalid request") end end -- cgit v1.2.3 From 987674235814205344d320c0e4c21df17b1cdd15 Mon Sep 17 00:00:00 2001 From: duponin Date: Sun, 11 Dec 2022 23:15:08 +0100 Subject: Return 413 when an actor's banner or background exceeds the size limit --- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 656a861cd..ea6e593d9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -257,6 +257,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} -> render_error(conn, :request_entity_too_large, "File is too large") + {:error, %Ecto.Changeset{errors: [banner: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + + {:error, %Ecto.Changeset{errors: [background: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + _e -> render_error(conn, :forbidden, "Invalid request") end -- cgit v1.2.3 From 62c27e016441d8f0649fea0ea9cfe0646e2b6f11 Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 14 Dec 2022 01:04:42 -0500 Subject: Fix failure when registering a user with no email when approval required --- lib/pleroma/user.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index b422e5c1d..d2ae08888 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -905,7 +905,7 @@ defmodule Pleroma.User do end end - defp send_user_approval_email(user) do + defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do user |> Pleroma.Emails.UserEmail.approval_pending_email() |> Pleroma.Emails.Mailer.deliver_async() @@ -913,6 +913,10 @@ defmodule Pleroma.User do {:ok, :enqueued} end + defp send_user_approval_email(_user) do + {:ok, :skipped} + end + defp send_admin_approval_emails(user) do all_superusers() |> Enum.filter(fn user -> not is_nil(user.email) end) -- cgit v1.2.3 From 9838790a7d14e72449af3ae53daf3df30fd1aba0 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 15 Dec 2022 17:46:20 -0500 Subject: AttachmentValidator: Actually require url --- .../web/activity_pub/object_validators/attachment_validator.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index 14f51e2c5..398020bff 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -45,9 +45,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do struct |> cast(data, [:id, :type, :mediaType, :name, :blurhash]) - |> cast_embed(:url, with: &url_changeset/2) + |> cast_embed(:url, with: &url_changeset/2, required: true) |> validate_inclusion(:type, ~w[Link Document Audio Image Video]) - |> validate_required([:type, :mediaType, :url]) + |> validate_required([:type, :mediaType]) end def url_changeset(struct, data) do @@ -91,6 +91,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do defp validate_data(cng) do cng |> validate_inclusion(:type, ~w[Document Audio Image Video]) - |> validate_required([:mediaType, :url, :type]) + |> validate_required([:mediaType, :type]) end end -- cgit v1.2.3 From 4a32b584e148a5625a983e0932f2a3e16fdbbf30 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 15 Dec 2022 18:02:33 -0500 Subject: StatusView: Fix warning --- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index b949d8f9a..0a8c98b44 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -65,7 +65,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do # This should be removed in a future version of Pleroma. Pleroma-FE currently # depends on this field, as well. defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do - use Bitwise + import Bitwise :erlang.crc32(context) |> band(bnot(0x8000_0000)) -- cgit v1.2.3 From bb27e4134b67e406810e40bbefd99fe392c25fed Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Thu, 15 Dec 2022 18:06:28 -0500 Subject: AudioVideoValidator: Fix embedded attachment requirements --- .../web/activity_pub/object_validators/audio_video_validator.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex index 432bd9039..671a7ef0c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -104,14 +104,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do struct |> cast(data, __schema__(:fields) -- [:attachment, :tag]) - |> cast_embed(:attachment) + |> cast_embed(:attachment, required: true) |> cast_embed(:tag) end defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Audio", "Video"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() -- cgit v1.2.3