From 0af77b20c19240479ea287446cc1c96a67318b2e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 11 Sep 2021 22:11:18 -0400 Subject: [PATCH 01/14] Implement moving account Ref: emit-move --- .../operations/twitter_util_operation.ex | 35 +++++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 33 ++++- .../web/twitter_api/util_controller_test.exs | 135 ++++++++++++++++++ 4 files changed, 203 insertions(+), 1 deletion(-) 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 @@ def captcha_operation 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 @@ def disable_account(%{assigns: %{user: user}} = conn, params) 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 diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index ee658ddf6..9b1181e1c 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -516,4 +516,139 @@ test "with proper permissions and valid password (JSON body)", %{conn: conn, use assert user.password_hash == nil end end + + describe "POST /api/pleroma/move_account" do + setup do: oauth_access(["write:accounts"]) + + test "without permissions", %{conn: conn} do + target_user = insert(:user) + target_nick = target_user |> User.full_nickname() + + conn = + conn + |> assign(:token, nil) + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "hi", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: write:accounts." + } + end + + test "with proper permissions and invalid password", %{conn: conn} do + target_user = insert(:user) + target_nick = target_user |> User.full_nickname() + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "hi", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."} + end + + test "with proper permissions, valid password and target account does not alias this", + %{ + conn: conn + } do + target_user = insert(:user) + target_nick = target_user |> User.full_nickname() + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "test", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "error" => "Target account must have the origin in `alsoKnownAs`" + } + end + + test "with proper permissions, valid password and target account aliases this", %{ + conn: conn, + user: user + } do + target_user = insert(:user, also_known_as: [user.ap_id]) + target_nick = target_user |> User.full_nickname() + follower = insert(:user) + + User.follow(follower, user) + + assert User.following?(follower, user) + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post( + "/api/pleroma/move_account", + %{ + password: "test", + target_account: target_nick + } + ) + + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} + + params = %{ + "op" => "move_following", + "origin_id" => user.id, + "target_id" => target_user.id + } + + assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) + + Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) + + refute User.following?(follower, user) + assert User.following?(follower, target_user) + end + + test "prefix nickname by @ should work", %{ + conn: conn, + user: user + } do + target_user = insert(:user, also_known_as: [user.ap_id]) + target_nick = target_user |> User.full_nickname() + follower = insert(:user) + + User.follow(follower, user) + + assert User.following?(follower, user) + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post( + "/api/pleroma/move_account", + %{ + password: "test", + target_account: "@" <> target_nick + } + ) + + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} + + params = %{ + "op" => "move_following", + "origin_id" => user.id, + "target_id" => target_user.id + } + + assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) + + Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) + + refute User.following?(follower, user) + assert User.following?(follower, target_user) + end + end end From df90b3e66ab3d53f727a7978696e32fe01d48f0f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 00:10:36 -0400 Subject: [PATCH 02/14] Document move_account API Ref: emit-move --- docs/development/API/pleroma_api.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 0e7367a72..cd3cd3643 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -341,6 +341,16 @@ See [Admin-API](admin_api.md) * Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise * Note: Currently, Mastodon has no API for changing email. If they add it in future it might be incompatible with Pleroma. +## `/api/pleroma/move_account` +### Move account +* Method `POST` +* Authentication: required +* Params: + * `password`: user's password + * `target_account`: the nickname of the target account (e.g. `foo@example.org`) +* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise +* Note: This endpoint emits a `Move` activity to all followers of the current account. Some remote servers will automatically unfollow the current account and follow the target account upon seeing this, but this depends on the remote server implementation and cannot be guaranteed. For local followers , they will automatically unfollow and follow if and only if they have set the `allow_following_move` preference ("Allow auto-follow when following account moves"). + # Pleroma Conversations Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints: From 3092558bc1cef6cbea12284de17c13175b8d97ab Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 00:12:05 -0400 Subject: [PATCH 03/14] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e527f32de..4dcebb16c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. - AdminAPI: allow moderators to manage reports, users, invites, and custom emojis - AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc +- Added move account API ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies From 60081a88181e7af9b6110e9b7456e56f33c16f6b Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 09:40:20 -0400 Subject: [PATCH 04/14] Add User.add_alias/2 and User.alias_users/1 Ref: emit-move --- lib/pleroma/user.ex | 19 ++++++++++++++ test/pleroma/user_test.exs | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) 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 @@ def change_email(user, email) 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 diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 6cd93c34c..9971aba66 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -2498,4 +2498,56 @@ defp object_id_from_created_activity(user) do %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id) object_id end + + describe "add_alias/2" do + test "should add alias for another user" do + user = insert(:user) + user2 = insert(:user) + + assert {:ok, user_updated} = user |> User.add_alias(user2) + + assert user_updated.also_known_as |> length() == 1 + assert user2.ap_id in user_updated.also_known_as + end + + test "should add multiple aliases" do + user = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + + assert {:ok, user} = user |> User.add_alias(user2) + assert {:ok, user_updated} = user |> User.add_alias(user3) + + assert user_updated.also_known_as |> length() == 2 + assert user2.ap_id in user_updated.also_known_as + assert user3.ap_id in user_updated.also_known_as + end + + test "should not add duplicate aliases" do + user = insert(:user) + user2 = insert(:user) + + assert {:ok, user} = user |> User.add_alias(user2) + + assert {:ok, user_updated} = user |> User.add_alias(user2) + + assert user_updated.also_known_as |> length() == 1 + assert user2.ap_id in user_updated.also_known_as + end + end + + describe "alias_users/1" do + test "should get aliases for a user" do + user = insert(:user) + user2 = insert(:user, also_known_as: [user.ap_id]) + + aliases = user2 |> User.alias_users() + + assert aliases |> length() == 1 + + alias_user = aliases |> Enum.at(0) + + assert alias_user.ap_id == user.ap_id + end + end end From c1aa3c98ac923d0f1a032ef0e171ed4e27ae1453 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 11:46:37 -0400 Subject: [PATCH 05/14] Add get and add aliases endpoints Ref: emit-move --- .../operations/twitter_util_operation.ex | 63 +++++++++++++++ lib/pleroma/web/router.ex | 3 + .../controllers/util_controller.ex | 29 ++++++- .../web/twitter_api/util_controller_test.exs | 79 +++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) 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 @@ defp move_account_request 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 @@ def move_account(%{assigns: %{user: user}, body_params: body_params} = conn, %{} 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) diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 9b1181e1c..4a1bba5c6 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -651,4 +651,83 @@ test "prefix nickname by @ should work", %{ assert User.following?(follower, target_user) end end + + describe "GET /api/pleroma/aliases" do + setup do: oauth_access(["read:accounts"]) + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> get("/api/pleroma/aliases") + + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: read:accounts." + } + end + + test "with permissions", %{ + conn: conn + } do + assert %{"aliases" => []} = conn + |> get("/api/pleroma/aliases") + |> json_response_and_validate_schema(200) + end + + test "with permissions and aliases", %{} do + user = insert(:user) + user2 = insert(:user) + + assert {:ok, user} = user |> User.add_alias(user2) + + %{user: _user, conn: conn} = oauth_access(["read:accounts"], user: user) + + assert %{"aliases" => aliases} = conn + |> get("/api/pleroma/aliases") + |> json_response_and_validate_schema(200) + assert aliases == [user2 |> User.full_nickname()] + end + end + + describe "PUT /api/pleroma/aliases" do + setup do: oauth_access(["write:accounts"]) + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/aliases", %{alias: "none"}) + + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: write:accounts." + } + end + + test "with permissions, no alias param", %{ + conn: conn + } do + conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/aliases", %{}) + + assert %{"error" => "Missing field: alias."} = json_response_and_validate_schema(conn, 400) + end + + test "with permissions, with alias param", %{ + conn: conn + } do + user2 = insert(:user) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/aliases", %{alias: user2 |> User.full_nickname()}) + + assert json_response_and_validate_schema(conn, 200) == %{ + "status" => "success" + } + end + end end From 54d7b4354ce6d8da87831614855afc8e8dcc6aae Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 12:26:32 -0400 Subject: [PATCH 06/14] Add deleting alias endpoint Ref: emit-move --- lib/pleroma/user.ex | 13 ++++ .../operations/twitter_util_operation.ex | 40 ++++++++++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 21 +++++- test/pleroma/user_test.exs | 24 ++++++ .../web/twitter_api/util_controller_test.exs | 73 +++++++++++++++++-- 6 files changed, 164 insertions(+), 8 deletions(-) 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 @@ def add_alias(user, new_alias_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 @@ defp add_alias_request 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 @@ def add_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) 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) diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 9971aba66..984a837e2 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -2550,4 +2550,28 @@ test "should get aliases for a user" do assert alias_user.ap_id == user.ap_id end end + + describe "delete_alias/2" do + test "should delete existing alias" do + user = insert(:user) + user2 = insert(:user, also_known_as: [user.ap_id]) + + assert {:ok, user_updated} = user2 |> User.delete_alias(user) + + assert user_updated.also_known_as == [] + end + + test "should report error on non-existing alias" do + user = insert(:user) + user2 = insert(:user) + user3 = insert(:user, also_known_as: [user.ap_id]) + + assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2) + + user3_updated = User.get_cached_by_ap_id(user3.ap_id) + + assert user3_updated.also_known_as |> length() == 1 + assert user.ap_id in user3_updated.also_known_as + end + end end diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 4a1bba5c6..40fcadb72 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -669,9 +669,10 @@ test "without permissions", %{conn: conn} do test "with permissions", %{ conn: conn } do - assert %{"aliases" => []} = conn - |> get("/api/pleroma/aliases") - |> json_response_and_validate_schema(200) + assert %{"aliases" => []} = + conn + |> get("/api/pleroma/aliases") + |> json_response_and_validate_schema(200) end test "with permissions and aliases", %{} do @@ -682,9 +683,11 @@ test "with permissions and aliases", %{} do %{user: _user, conn: conn} = oauth_access(["read:accounts"], user: user) - assert %{"aliases" => aliases} = conn - |> get("/api/pleroma/aliases") - |> json_response_and_validate_schema(200) + assert %{"aliases" => aliases} = + conn + |> get("/api/pleroma/aliases") + |> json_response_and_validate_schema(200) + assert aliases == [user2 |> User.full_nickname()] end end @@ -730,4 +733,62 @@ test "with permissions, with alias param", %{ } end end + + describe "DELETE /api/pleroma/aliases" do + setup do + alias_user = insert(:user) + non_alias_user = insert(:user) + user = insert(:user, also_known_as: [alias_user.ap_id]) + + oauth_access(["write:accounts"], user: user) + |> Map.put(:alias_user, alias_user) + |> Map.put(:non_alias_user, non_alias_user) + end + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/aliases", %{alias: "none"}) + + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: write:accounts." + } + end + + test "with permissions, no alias param", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/aliases", %{}) + + assert %{"error" => "Missing field: alias."} = json_response_and_validate_schema(conn, 400) + end + + test "with permissions, account does not have such alias", %{ + conn: conn, + non_alias_user: non_alias_user + } do + conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/aliases", %{alias: non_alias_user |> User.full_nickname()}) + + assert %{"error" => "Account has no such alias."} = + json_response_and_validate_schema(conn, 404) + end + + test "with permissions, account does have such alias", %{ + conn: conn, + alias_user: alias_user + } do + conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/aliases", %{alias: alias_user |> User.full_nickname()}) + + assert %{"status" => "success"} = json_response_and_validate_schema(conn, 200) + end + end end From 1d8abf2511317927794c0c9f5f5c2a6efdb66fed Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 12:35:57 -0400 Subject: [PATCH 07/14] Document aliases endpoints Ref: emit-move --- docs/development/API/pleroma_api.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index cd3cd3643..b67c9c4c7 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -351,6 +351,26 @@ See [Admin-API](admin_api.md) * Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise * Note: This endpoint emits a `Move` activity to all followers of the current account. Some remote servers will automatically unfollow the current account and follow the target account upon seeing this, but this depends on the remote server implementation and cannot be guaranteed. For local followers , they will automatically unfollow and follow if and only if they have set the `allow_following_move` preference ("Allow auto-follow when following account moves"). +## `/api/pleroma/aliases` +### Get aliases of the current account +* Method `GET` +* Authentication: required +* Response: JSON. Returns `{"aliases": [alias, ...]}`, where `alias` is the nickname of an alias, e.g. `foo@example.org`. + +### Add alias to the current account +* Method `PUT` +* Authentication: required +* Params: + * `alias`: the nickname of the alias to add, e.g. `foo@example.org`. +* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise + +### Delete alias from the current account +* Method `DELETE` +* Authentication: required +* Params: + * `alias`: the nickname of the alias to delete, e.g. `foo@example.org`. +* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise + # Pleroma Conversations Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints: From e41eee5ed1c4e7001a28dababe046e28357d2ffd Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 16:45:17 -0400 Subject: [PATCH 08/14] Make Move activity federate properly Ref: emit-move --- lib/pleroma/web/activity_pub/activity_pub.ex | 3 ++- test/pleroma/web/activity_pub/activity_pub_test.exs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) 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 @@ def move(%User{} = origin, %User{} = target, local \\ true) 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, diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 574ef0d71..3021f2995 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1739,9 +1739,12 @@ test "create" do "target" => ^new_ap_id, "type" => "Move" }, - local: true + local: true, + recipients: recipients } = activity + assert old_user.follower_address in recipients + params = %{ "op" => "move_following", "origin_id" => old_user.id, From 4f44fd32eae100d5ce74b3c9bd5457858f145198 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 12 Sep 2021 21:52:44 -0400 Subject: [PATCH 09/14] 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 +- .../web/activity_pub/activity_pub_test.exs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) 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 @@ def move_following(origin, target) 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 diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 3021f2995..dec28f81b 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1775,6 +1775,42 @@ test "old user must be in the new user's `also_known_as` list" do assert {:error, "Target account must have the origin in `alsoKnownAs`"} = ActivityPub.move(old_user, new_user) end + + test "do not move remote user following relationships" do + %{ap_id: old_ap_id} = old_user = insert(:user) + %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id]) + follower_remote = insert(:user, local: false) + + User.follow(follower_remote, old_user) + + assert User.following?(follower_remote, old_user) + + assert {:ok, activity} = ActivityPub.move(old_user, new_user) + + assert %Activity{ + actor: ^old_ap_id, + data: %{ + "actor" => ^old_ap_id, + "object" => ^old_ap_id, + "target" => ^new_ap_id, + "type" => "Move" + }, + local: true + } = activity + + params = %{ + "op" => "move_following", + "origin_id" => old_user.id, + "target_id" => new_user.id + } + + assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) + + Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) + + assert User.following?(follower_remote, old_user) + refute User.following?(follower_remote, new_user) + end end test "doesn't retrieve replies activities with exclude_replies" do From a677c621e822673b3b2922d5b0975f704f2f59a7 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 15:30:04 -0400 Subject: [PATCH 10/14] Make move_following worker follow then unfollow Ref: emit-move --- lib/pleroma/following_relationship.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 @@ def move_following(origin, target) 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 [] -> From eb383ef8d366c1656494278dfe6d2a6afdc04bc6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 16:26:22 -0400 Subject: [PATCH 11/14] Make move_account endpoint process non-existent users properly Ref: emit-move --- .../operations/twitter_util_operation.ex | 3 ++- .../controllers/util_controller.ex | 23 ++++++++++++++++--- .../web/twitter_api/util_controller_test.exs | 19 +++++++++++++++ test/support/http_request_mock.ex | 9 ++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) 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 @@ def move_account_operation 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 @@ def disable_account(%{assigns: %{user: user}} = conn, params) 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 @@ defp find_user_by_nickname(nickname) 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 diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 40fcadb72..7f4a343ff 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -573,6 +573,25 @@ test "with proper permissions, valid password and target account does not alias } end + test "with proper permissions, valid password and target account does not exist", + %{ + conn: conn + } do + target_nick = "not_found@mastodon.social" + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "test", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 404) == %{ + "error" => "Target account not found." + } + end + test "with proper permissions, valid password and target account aliases this", %{ conn: conn, user: user diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 94900dc14..7d5ebd2de 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -725,6 +725,15 @@ def get( }} end + def get( + "https://mastodon.social/.well-known/webfinger?resource=acct:not_found@mastodon.social", + _, + _, + [{"accept", "application/xrd+xml,application/jrd+json"}] + ) do + {:ok, %Tesla.Env{status: 404}} + end + def get("http://gs.example.org/.well-known/host-meta", _, _, _) do {:ok, %Tesla.Env{ From 3fd13b70ec7c8e9faca46550fe02b51ccbeaebd0 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 18:26:55 -0400 Subject: [PATCH 12/14] Test that the target account is re-fetched in move_account Ref: emit-move --- .../https___lm.kazv.moe_users_mewmew.xml | 1 + .../fixtures/tesla_mock/lm.kazv.moe_host_meta | 1 + .../tesla_mock/mewmew@lm.kazv.moe.json | 1 + .../web/twitter_api/util_controller_test.exs | 73 +++++++++++++++++++ test/support/http_request_mock.ex | 51 +++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 test/fixtures/tesla_mock/https___lm.kazv.moe_users_mewmew.xml create mode 100644 test/fixtures/tesla_mock/lm.kazv.moe_host_meta create mode 100644 test/fixtures/tesla_mock/mewmew@lm.kazv.moe.json diff --git a/test/fixtures/tesla_mock/https___lm.kazv.moe_users_mewmew.xml b/test/fixtures/tesla_mock/https___lm.kazv.moe_users_mewmew.xml new file mode 100644 index 000000000..b9e8dbbf5 --- /dev/null +++ b/test/fixtures/tesla_mock/https___lm.kazv.moe_users_mewmew.xml @@ -0,0 +1 @@ +acct:mewmew@lm.kazv.moehttps://lm.kazv.moe/users/mewmewhttps://lm.kazv.moe/users/testerhttps://lm.kazv.moe/users/testuser diff --git a/test/fixtures/tesla_mock/lm.kazv.moe_host_meta b/test/fixtures/tesla_mock/lm.kazv.moe_host_meta new file mode 100644 index 000000000..02e6f055e --- /dev/null +++ b/test/fixtures/tesla_mock/lm.kazv.moe_host_meta @@ -0,0 +1 @@ + diff --git a/test/fixtures/tesla_mock/mewmew@lm.kazv.moe.json b/test/fixtures/tesla_mock/mewmew@lm.kazv.moe.json new file mode 100644 index 000000000..8d2c3e1e7 --- /dev/null +++ b/test/fixtures/tesla_mock/mewmew@lm.kazv.moe.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://lm.kazv.moe/schemas/litepub-0.1.jsonld",{"@language":"und"}],"alsoKnownAs":["https://lm.kazv.moe/users/tester","https://lm.kazv.moe/users/testuser"],"attachment":[],"capabilities":{"acceptsChatMessages":true},"discoverable":false,"endpoints":{"oauthAuthorizationEndpoint":"https://lm.kazv.moe/oauth/authorize","oauthRegistrationEndpoint":"https://lm.kazv.moe/api/v1/apps","oauthTokenEndpoint":"https://lm.kazv.moe/oauth/token","sharedInbox":"https://lm.kazv.moe/inbox","uploadMedia":"https://lm.kazv.moe/api/ap/upload_media"},"featured":"https://lm.kazv.moe/users/mewmew/collections/featured","followers":"https://lm.kazv.moe/users/mewmew/followers","following":"https://lm.kazv.moe/users/mewmew/following","id":"https://lm.kazv.moe/users/mewmew","inbox":"https://lm.kazv.moe/users/mewmew/inbox","manuallyApprovesFollowers":false,"name":"mew","outbox":"https://lm.kazv.moe/users/mewmew/outbox","preferredUsername":"mewmew","publicKey":{"id":"https://lm.kazv.moe/users/mewmew#main-key","owner":"https://lm.kazv.moe/users/mewmew","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0nT3IVUwx799FSJyJEOY\n5D2c5zgtt2Z+BD9417eVLmVQF5fJlWgcKS4pbFc76zkYoBkZtV7XbzvN9KTNulpa\nUGNOM0/UdEoQLB8xbVCMm0ABUU8vbTWoMTxp93bfVHBz+33FPYdH1JHX4TCU/mJF\nX4UJMvFmMn5BFjSQm9GG6Eq2j6SAUsaTa8+Rrd8FzS6zb/dk3N/Llz0tfsZYS0sq\nEy9OYhsKOQ6eegULFJOF3Hz04vzwftmeXFsbb3aO2zKz3uAMYZglWHNBYJAePBtJ\ng362kqdJwgT14TFnZ0K2ziDPbkRULG1Kke/lsqw2rPF6Q6P4PeO1shCEDthoDoID\newIDAQAB\n-----END PUBLIC KEY-----\n\n"},"summary":"","tag":[],"type":"Person","url":"https://lm.kazv.moe/users/mewmew"} diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 7f4a343ff..fb7da93f8 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -592,6 +592,79 @@ test "with proper permissions, valid password and target account does not exist" } end + test "with proper permissions, valid password, remote target account aliases this and local cache does not exist", + %{} do + user = insert(:user, ap_id: "https://lm.kazv.moe/users/testuser") + %{user: _user, conn: conn} = oauth_access(["write:accounts"], user: user) + + target_nick = "mewmew@lm.kazv.moe" + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "test", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} + end + + test "with proper permissions, valid password, remote target account aliases this and local cache does not alias this", + %{} do + user = insert(:user, ap_id: "https://lm.kazv.moe/users/testuser") + %{user: _user, conn: conn} = oauth_access(["write:accounts"], user: user) + + target_user = + insert( + :user, + ap_id: "https://lm.kazv.moe/users/mewmew", + nickname: "mewmew@lm.kazv.moe", + local: false + ) + + target_nick = target_user |> User.full_nickname() + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "test", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} + end + + test "with proper permissions, valid password, remote target account does not alias this and local cache aliases this", + %{ + user: user, + conn: conn + } do + target_user = + insert( + :user, + ap_id: "https://lm.kazv.moe/users/mewmew", + nickname: "mewmew@lm.kazv.moe", + local: false, + also_known_as: [user.ap_id] + ) + + target_nick = target_user |> User.full_nickname() + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/move_account", %{ + "password" => "test", + "target_account" => target_nick + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "error" => "Target account must have the origin in `alsoKnownAs`" + } + end + test "with proper permissions, valid password and target account aliases this", %{ conn: conn, user: user diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 7d5ebd2de..dfac773de 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -1133,6 +1133,57 @@ def get( }} end + def get("http://lm.kazv.moe/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/lm.kazv.moe_host_meta") + }} + end + + def get("https://lm.kazv.moe/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/lm.kazv.moe_host_meta") + }} + end + + def get( + "https://lm.kazv.moe/.well-known/webfinger?resource=acct:mewmew@lm.kazv.moe", + _, + _, + [{"accept", "application/xrd+xml,application/jrd+json"}] + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/https___lm.kazv.moe_users_mewmew.xml"), + headers: [{"content-type", "application/xrd+xml"}] + }} + end + + def get("https://lm.kazv.moe/users/mewmew", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mewmew@lm.kazv.moe.json"), + headers: activitypub_object_headers() + }} + end + + def get("https://lm.kazv.moe/users/mewmew/collections/featured", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "lm.kazv.moe") + |> String.replace("{{nickname}}", "mewmew"), + headers: [{"content-type", "application/activity+json"}] + }} + end + def get("https://info.pleroma.site/activity.json", _, _, [ {"accept", "application/activity+json"} ]) do From 9a27cb4f9d314fe1066f566de71357f55926116e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Sep 2021 19:27:04 -0400 Subject: [PATCH 13/14] Deal with target not found error in add_alias Ref: emit-move --- .../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(-) 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 @@ def add_alias_operation 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 @@ def add_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) 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 From 5ef2dc317d49453153855f106fa098625b6e55ae Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Sun, 31 Jul 2022 21:34:23 +0000 Subject: [PATCH 14/14] Change test case wording --- test/pleroma/web/twitter_api/util_controller_test.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 7d7eb39ff..5dc72b177 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -553,7 +553,7 @@ test "with proper permissions and invalid password", %{conn: conn} do assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."} end - test "with proper permissions, valid password and target account does not alias this", + test "with proper permissions, valid password and target account does not alias it", %{ conn: conn } do @@ -592,7 +592,7 @@ test "with proper permissions, valid password and target account does not exist" } end - test "with proper permissions, valid password, remote target account aliases this and local cache does not exist", + test "with proper permissions, valid password, remote target account aliases it and local cache does not exist", %{} do user = insert(:user, ap_id: "https://lm.kazv.moe/users/testuser") %{user: _user, conn: conn} = oauth_access(["write:accounts"], user: user) @@ -610,7 +610,7 @@ test "with proper permissions, valid password, remote target account aliases thi assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} end - test "with proper permissions, valid password, remote target account aliases this and local cache does not alias this", + test "with proper permissions, valid password, remote target account aliases it and local cache does not aliases it", %{} do user = insert(:user, ap_id: "https://lm.kazv.moe/users/testuser") %{user: _user, conn: conn} = oauth_access(["write:accounts"], user: user) @@ -636,7 +636,7 @@ test "with proper permissions, valid password, remote target account aliases thi assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} end - test "with proper permissions, valid password, remote target account does not alias this and local cache aliases this", + test "with proper permissions, valid password, remote target account does not aliases it and local cache aliases it", %{ user: user, conn: conn @@ -665,7 +665,7 @@ test "with proper permissions, valid password, remote target account does not al } end - test "with proper permissions, valid password and target account aliases this", %{ + test "with proper permissions, valid password and target account aliases it", %{ conn: conn, user: user } do