From 7eecc3b61d6da64e0bfdc5b155cba0dae07b84d5 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 16 Feb 2021 23:23:35 +0100 Subject: [PATCH 01/15] OpenAPI: MastodonAPI Timeline Controller --- .../api_spec/operations/timeline_operation.ex | 3 ++- .../controllers/timeline_controller_test.exs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index cae18c758..24d792916 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -115,7 +115,8 @@ def hashtag_operation do ], operationId: "TimelineController.hashtag", responses: %{ - 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()), + 401 => Operation.response("Error", "application/json", ApiError) } } end diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index cc409451c..ed1286675 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -905,10 +905,10 @@ defp ensure_authenticated_access(base_uri) do %{conn: auth_conn} = oauth_access(["read:statuses"]) res_conn = get(auth_conn, "#{base_uri}?local=true") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 res_conn = get(auth_conn, "#{base_uri}?local=false") - assert length(json_response(res_conn, 200)) == 2 + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end test "with default settings on private instances, returns 403 for unauthenticated users", %{ @@ -922,7 +922,7 @@ test "with default settings on private instances, returns 403 for unauthenticate for local <- [true, false] do res_conn = get(conn, "#{base_uri}?local=#{local}") - assert json_response(res_conn, :unauthorized) == error_response + assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response end ensure_authenticated_access(base_uri) @@ -939,7 +939,7 @@ test "with `%{local: true, federated: true}`, returns 403 for unauthenticated us for local <- [true, false] do res_conn = get(conn, "#{base_uri}?local=#{local}") - assert json_response(res_conn, :unauthorized) == error_response + assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response end ensure_authenticated_access(base_uri) @@ -951,10 +951,10 @@ test "with `%{local: false, federated: true}`, forbids unauthenticated access to clear_config([:restrict_unauthenticated, :timelines, :federated], true) res_conn = get(conn, "#{base_uri}?local=true") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 res_conn = get(conn, "#{base_uri}?local=false") - assert json_response(res_conn, :unauthorized) == error_response + assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response ensure_authenticated_access(base_uri) end @@ -966,11 +966,11 @@ test "with `%{local: true, federated: false}`, forbids unauthenticated access to clear_config([:restrict_unauthenticated, :timelines, :federated], false) res_conn = get(conn, "#{base_uri}?local=true") - assert json_response(res_conn, :unauthorized) == error_response + assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response # Note: local activities get delivered as part of federated timeline res_conn = get(conn, "#{base_uri}?local=false") - assert length(json_response(res_conn, 200)) == 2 + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 ensure_authenticated_access(base_uri) end From 3123ecdd6e7a189f815624ee78be4f62487aa3db Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 16 Feb 2021 23:37:16 +0100 Subject: [PATCH 02/15] OpenAPI: MastodonAPI Media Controller --- lib/pleroma/web/api_spec/operations/media_operation.ex | 1 + .../web/mastodon_api/controllers/media_controller_test.exs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex index 85aa14869..1e245b291 100644 --- a/lib/pleroma/web/api_spec/operations/media_operation.ex +++ b/lib/pleroma/web/api_spec/operations/media_operation.ex @@ -105,6 +105,7 @@ def show_operation do responses: %{ 200 => Operation.response("Media", "application/json", Attachment), 401 => Operation.response("Media", "application/json", ApiError), + 403 => Operation.response("Media", "application/json", ApiError), 422 => Operation.response("Media", "application/json", ApiError) } } diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index 6c8f984d5..39d7f99f6 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -140,7 +140,7 @@ test "it returns 403 if media object requested by non-owner", %{object: object, conn |> get("/api/v1/media/#{object.id}") - |> json_response(403) + |> json_response_and_validate_schema(403) end end end From e47f83cfc822716c00f3fcaffe73f31208749601 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 16 Feb 2021 23:39:07 +0100 Subject: [PATCH 03/15] OpenAPI: MastodonAPI Conversation Controller --- .../mastodon_api/controllers/conversation_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs index 3176f1296..00797a9ea 100644 --- a/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs @@ -214,7 +214,8 @@ test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context") - assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) + assert %{"ancestors" => [], "descendants" => []} == + json_response_and_validate_schema(res_conn, 200) end test "Removes a conversation", %{user: user_one, conn: conn} do From 3a8404820d803ccea44071178cc90f6aafcee80b Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 16 Feb 2021 23:40:50 +0100 Subject: [PATCH 04/15] Verify MastoFE Controller put_settings response --- test/pleroma/web/mastodon_api/masto_fe_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs index ea66c708f..e679d781a 100644 --- a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs +++ b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs @@ -20,7 +20,7 @@ test "put settings", %{conn: conn} do |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"])) |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) - assert _result = json_response(conn, 200) + assert %{} = json_response(conn, 200) user = User.get_cached_by_ap_id(user.ap_id) assert user.mastofe_settings == %{"programming" => "socks"} From 0c7c6463d13b8a4471b8721912c82fe1cbe3e91a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 17 Feb 2021 00:35:26 +0100 Subject: [PATCH 05/15] OpenAPI: MastodonAPI Account Controller, excluding OAuth --- .../controllers/account_controller_test.exs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index a327c0d1d..3036e25b3 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -514,11 +514,11 @@ test "paginates a user's statuses", %{user: user, conn: conn} do {:ok, post_2} = CommonAPI.post(user, %{status: "second post"}) response_1 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1") - assert [res] = json_response(response_1, 200) + assert [res] = json_response_and_validate_schema(response_1, 200) assert res["id"] == post_2.id response_2 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1&max_id=#{res["id"]}") - assert [res] = json_response(response_2, 200) + assert [res] = json_response_and_validate_schema(response_2, 200) assert res["id"] == post_1.id refute response_1 == response_2 @@ -881,7 +881,7 @@ test "following without reblogs" do assert [] == conn |> get("/api/v1/timelines/home") - |> json_response(200) + |> json_response_and_validate_schema(200) assert %{"showing_reblogs" => true} = conn @@ -892,7 +892,7 @@ test "following without reblogs" do assert [%{"id" => ^reblog_id}] = conn |> get("/api/v1/timelines/home") - |> json_response(200) + |> json_response_and_validate_schema(200) end test "following with reblogs" do @@ -910,7 +910,7 @@ test "following with reblogs" do assert [%{"id" => ^reblog_id}] = conn |> get("/api/v1/timelines/home") - |> json_response(200) + |> json_response_and_validate_schema(200) assert %{"showing_reblogs" => false} = conn @@ -921,7 +921,7 @@ test "following with reblogs" do assert [] == conn |> get("/api/v1/timelines/home") - |> json_response(200) + |> json_response_and_validate_schema(200) end test "following / unfollowing errors", %{user: user, conn: conn} do From ef5de5eb398b6d4cbc1ed338f2f41d3bfa1c5fe9 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 17 Feb 2021 00:45:01 +0100 Subject: [PATCH 06/15] OpenAPI: MastodonAPI Status Controller --- .../web/mastodon_api/controllers/status_controller_test.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index f616f405e..4c0149a4c 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -81,6 +81,7 @@ test "posting a status", %{conn: conn} do "sensitive" => 0 }) + # Idempotency plug response means detection fail assert %{"id" => second_id} = json_response(conn_two, 200) assert id == second_id @@ -1542,7 +1543,7 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"])) |> get("api/v1/timelines/home") - [reblogged_activity] = json_response(conn3, 200) + [reblogged_activity] = json_response_and_validate_schema(conn3, 200) assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id @@ -1896,7 +1897,7 @@ test "posting a local only status" do local = Pleroma.Constants.as_local_public() assert %{"content" => "cofe", "id" => id, "visibility" => "local"} = - json_response(conn_one, 200) + json_response_and_validate_schema(conn_one, 200) assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id) end From e4743847a18cb7cbb9e607232f25eb1cf63a4551 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 17 Feb 2021 01:07:56 +0100 Subject: [PATCH 07/15] OpenAPI: PleromaAPI UserImport Controller --- lib/pleroma/web/api_spec/operations/user_import_operation.ex | 1 + .../web/pleroma_api/controllers/user_import_controller_test.exs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/user_import_operation.ex b/lib/pleroma/web/api_spec/operations/user_import_operation.ex index 6292e2004..8df19f1fc 100644 --- a/lib/pleroma/web/api_spec/operations/user_import_operation.ex +++ b/lib/pleroma/web/api_spec/operations/user_import_operation.ex @@ -23,6 +23,7 @@ def follow_operation do requestBody: request_body("Parameters", import_request(), required: true), responses: %{ 200 => ok_response(), + 403 => Operation.response("Error", "application/json", ApiError), 500 => Operation.response("Error", "application/json", ApiError) }, security: [%{"oAuth" => ["write:follow"]}] diff --git a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs index 25a7f8374..d977bc3a2 100644 --- a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs @@ -83,7 +83,7 @@ test "requires 'follow' or 'write:follows' permissions" do assert %{"error" => "Insufficient permissions: follow | write:follows."} == json_response(conn, 403) else - assert json_response(conn, 200) + assert json_response_and_validate_schema(conn, 200) end end end From a22c53810b36c5382c805e1c5ed7e1cf3d747ebc Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 17 Feb 2021 01:19:25 +0100 Subject: [PATCH 08/15] Remove deprecated /api/qvitter/statuses/notifications/read --- CHANGELOG.md | 3 ++ lib/pleroma/web/router.ex | 6 --- lib/pleroma/web/twitter_api/controller.ex | 33 ------------- .../web/twitter_api/controller_test.exs | 49 ------------------- 4 files changed, 3 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50484aaef..ce0bb1cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change. +### Removed +- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`) + ## Unreleased (Patch) ## [2.3.0] - 2020-03-01 diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index de0bd27d7..ce2d701d7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -620,12 +620,6 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) - - post( - "/qvitter/statuses/notifications/read", - TwitterAPI.Controller, - :mark_notifications_as_read - ) end scope "/", Pleroma.Web do diff --git a/lib/pleroma/web/twitter_api/controller.ex b/lib/pleroma/web/twitter_api/controller.ex index 077bfa70d..e32713311 100644 --- a/lib/pleroma/web/twitter_api/controller.ex +++ b/lib/pleroma/web/twitter_api/controller.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller - alias Pleroma.Notification alias Pleroma.User alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug @@ -14,11 +13,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do require Logger - plug( - OAuthScopesPlug, - %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read - ) - plug( :skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email @@ -67,31 +61,4 @@ defp json_reply(conn, status, json) do |> put_resp_content_type("application/json") |> send_resp(status, json) end - - def mark_notifications_as_read( - %{assigns: %{user: user}} = conn, - %{"latest_id" => latest_id} = params - ) do - Notification.set_read_up_to(user, latest_id) - - notifications = Notification.for_user(user, params) - - conn - # XXX: This is a hack because pleroma-fe still uses that API. - |> put_view(Pleroma.Web.MastodonAPI.NotificationView) - |> render("index.json", %{notifications: notifications, for: user}) - end - - def mark_notifications_as_read(%{assigns: %{user: _user}} = conn, _) do - bad_request_reply(conn, "You need to specify latest_id") - end - - defp bad_request_reply(conn, error_message) do - json = error_json(conn, error_message) - json_reply(conn, 400, json) - end - - defp error_json(conn, error_message) do - %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() - end end diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs index 583c904b2..bca9e2dad 100644 --- a/test/pleroma/web/twitter_api/controller_test.exs +++ b/test/pleroma/web/twitter_api/controller_test.exs @@ -7,59 +7,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Token import Pleroma.Factory - describe "POST /api/qvitter/statuses/notifications/read" do - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567}) - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials, without any params" do - %{conn: conn} = oauth_access(["write:notifications"]) - - conn = post(conn, "/api/qvitter/statuses/notifications/read") - - assert json_response(conn, 400) == %{ - "error" => "You need to specify latest_id", - "request" => "/api/qvitter/statuses/notifications/read" - } - end - - test "with credentials, with params" do - %{user: current_user, conn: conn} = - oauth_access(["read:notifications", "write:notifications"]) - - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(other_user, %{ - status: "Hey @#{current_user.nickname}" - }) - - response_conn = - conn - |> get("/api/v1/notifications") - - [notification] = json_response(response_conn, 200) - - assert notification["pleroma"]["is_seen"] == false - - response_conn = - conn - |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]}) - - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 - - assert notification["pleroma"]["is_seen"] == true - end - end - describe "GET /api/account/confirm_email/:id/:token" do setup do {:ok, user} = From 65cd9cb6384676c1660aa7f4da0f98ff7f43b999 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 17 Feb 2021 09:41:40 +0100 Subject: [PATCH 09/15] TwitterAPI: Remove unused read notification function --- .../web/twitter_api/controllers/util_controller.ex | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 940a645bb..60266aaab 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Config alias Pleroma.Emoji alias Pleroma.Healthcheck - alias Pleroma.Notification alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug @@ -30,7 +29,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do ] ) - plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do with %User{} = user <- User.get_cached_by_nickname(nick), @@ -62,17 +60,6 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil end end - def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do - with {:ok, _} <- Notification.read_one(user, notification_id) do - json(conn, %{status: "success"}) - else - {:error, message} -> - conn - |> put_resp_content_type("application/json") - |> send_resp(403, Jason.encode!(%{"error" => message})) - end - end - def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end From 55bdfb075c1cc5226948e3ff9d39fdae27aa9257 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 24 Feb 2021 23:40:33 +0100 Subject: [PATCH 10/15] OpenAPI: TwitterAPI Util Controller --- .../operations/twitter_util_operation.ex | 219 ++++++++++++++++++ .../controllers/util_controller.ex | 24 +- .../web/twitter_api/util_controller_test.exs | 204 +++++++++------- 3 files changed, 360 insertions(+), 87 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/twitter_util_operation.ex diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex new file mode 100644 index 000000000..62c9826f6 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -0,0 +1,219 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def emoji_operation do + %Operation{ + tags: ["Emojis"], + summary: "List all custom emojis", + operationId: "UtilController.emoji", + parameters: [], + responses: %{ + 200 => + Operation.response("List", "application/json", %Schema{ + type: :object, + additionalProperties: %Schema{ + type: :object, + properties: %{ + image_url: %Schema{type: :string}, + tags: %Schema{type: :array, items: %Schema{type: :string}} + } + }, + example: %{ + "firefox" => %{ + "image_url" => "/emoji/firefox.png", + "tag" => ["Fun"] + } + } + }) + } + } + end + + def frontend_configurations_operation do + %Operation{ + tags: ["Configuration"], + summary: "Dump frontend configurations", + operationId: "UtilController.frontend_configurations", + parameters: [], + responses: %{ + 200 => + Operation.response("List", "application/json", %Schema{ + type: :object, + additionalProperties: %Schema{type: :object} + }) + } + } + end + + def change_password_operation do + %Operation{ + tags: ["Accounts"], + summary: "Change account password", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.change_password", + parameters: [ + Operation.parameter(:password, :query, :string, "Current password", required: true), + Operation.parameter(:new_password, :query, :string, "New password", required: true), + Operation.parameter( + :new_password_confirmation, + :query, + :string, + "New password, confirmation", + 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 + + def change_email_operation do + %Operation{ + tags: ["Accounts"], + summary: "Change account email", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.change_email", + parameters: [ + Operation.parameter(:password, :query, :string, "Current password", required: true), + Operation.parameter(:email, :query, :string, "New email", required: true) + ], + requestBody: nil, + 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 + + def update_notificaton_settings_operation do + %Operation{ + tags: ["Accounts"], + summary: "Update Notification Settings", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.update_notificaton_settings", + parameters: [ + Operation.parameter( + :block_from_strangers, + :query, + BooleanLike, + "blocks notifications from accounts you do not follow" + ), + Operation.parameter( + :hide_notification_contents, + :query, + BooleanLike, + "removes the contents of a message from the push notification" + ) + ], + requestBody: nil, + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{status: %Schema{type: :string, example: "success"}} + }), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def disable_account_operation do + %Operation{ + tags: ["Accounts"], + summary: "Disable Account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.disable_account", + parameters: [ + Operation.parameter(:password, :query, :string, "Password") + ], + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{status: %Schema{type: :string, example: "success"}} + }), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def delete_account_operation do + %Operation{ + tags: ["Accounts"], + summary: "Delete Account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.delete_account", + parameters: [ + Operation.parameter(:password, :query, :string, "Password") + ], + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{status: %Schema{type: :string, example: "success"}} + }), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def captcha_operation do + %Operation{ + summary: "Get a captcha", + operationId: "UtilController.captcha", + parameters: [], + responses: %{ + 200 => Operation.response("Success", "application/json", %Schema{type: :object}) + } + } + end + + def healthcheck_operation do + %Operation{ + tags: ["Accounts"], + summary: "Disable Account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.healthcheck", + parameters: [], + responses: %{ + 200 => Operation.response("Healthy", "application/json", %Schema{type: :object}), + 503 => + Operation.response("Disabled or Unhealthy", "application/json", %Schema{type: :object}) + } + } + end + + def remote_subscribe_operation do + %Operation{ + tags: ["Accounts"], + summary: "Remote Subscribe", + operationId: "UtilController.remote_subscribe", + parameters: [], + responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} + } + 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 60266aaab..a2e69666e 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -15,6 +15,7 @@ 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( @@ -29,6 +30,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 with %User{} = user <- User.get_cached_by_nickname(nick), @@ -79,13 +81,17 @@ def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do end end - def change_password(%{assigns: %{user: user}} = conn, params) do - case CommonAPI.Utils.confirm_current_password(user, params["password"]) do + def change_password(%{assigns: %{user: user}} = conn, %{ + password: password, + new_password: new_password, + new_password_confirmation: new_password_confirmation + }) do + case CommonAPI.Utils.confirm_current_password(user, password) do {:ok, user} -> with {:ok, _user} <- User.reset_password(user, %{ - password: params["new_password"], - password_confirmation: params["new_password_confirmation"] + password: new_password, + password_confirmation: new_password_confirmation }) do json(conn, %{status: "success"}) else @@ -102,10 +108,10 @@ def change_password(%{assigns: %{user: user}} = conn, params) do end end - def change_email(%{assigns: %{user: user}} = conn, params) do - case CommonAPI.Utils.confirm_current_password(user, params["password"]) do + def change_email(%{assigns: %{user: user}} = conn, %{password: password, email: email}) do + case CommonAPI.Utils.confirm_current_password(user, password) do {:ok, user} -> - with {:ok, _user} <- User.change_email(user, params["email"]) do + with {:ok, _user} <- User.change_email(user, email) do json(conn, %{status: "success"}) else {:error, changeset} -> @@ -122,7 +128,7 @@ def change_email(%{assigns: %{user: user}} = conn, params) do end def delete_account(%{assigns: %{user: user}} = conn, params) do - password = params["password"] || "" + password = params[:password] || "" case CommonAPI.Utils.confirm_current_password(user, password) do {:ok, user} -> @@ -135,7 +141,7 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do end def disable_account(%{assigns: %{user: user}} = conn, params) do - case CommonAPI.Utils.confirm_current_password(user, params["password"]) do + case CommonAPI.Utils.confirm_current_password(user, params[:password]) do {:ok, user} -> User.set_activation_async(user, false) json(conn, %{status: "success"}) diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index bdbc478c3..cc17940b5 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -25,11 +25,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do test "it updates notification settings", %{user: user, conn: conn} do conn - |> put("/api/pleroma/notification_settings", %{ - "block_from_strangers" => true, - "bar" => 1 - }) - |> json_response(:ok) + |> put( + "/api/pleroma/notification_settings?#{ + URI.encode_query(%{ + block_from_strangers: true + }) + }" + ) + |> json_response_and_validate_schema(:ok) user = refresh_record(user) @@ -41,8 +44,14 @@ test "it updates notification settings", %{user: user, conn: conn} do test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do conn - |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) - |> json_response(:ok) + |> put( + "/api/pleroma/notification_settings?#{ + URI.encode_query(%{ + hide_notification_contents: 1 + }) + }" + ) + |> json_response_and_validate_schema(:ok) user = refresh_record(user) @@ -70,7 +79,7 @@ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} d response = conn |> get("/api/pleroma/frontend_configurations") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!() end @@ -81,7 +90,7 @@ test "returns json with custom emoji with tags", %{conn: conn} do emoji = conn |> get("/api/pleroma/emoji") - |> json_response(200) + |> json_response_and_validate_schema(200) assert Enum.all?(emoji, fn {_key, @@ -103,7 +112,7 @@ test "returns 503 when healthcheck disabled", %{conn: conn} do response = conn |> get("/api/pleroma/healthcheck") - |> json_response(503) + |> json_response_and_validate_schema(503) assert response == %{} end @@ -116,7 +125,7 @@ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do response = conn |> get("/api/pleroma/healthcheck") - |> json_response(200) + |> json_response_and_validate_schema(200) assert %{ "active" => _, @@ -136,7 +145,7 @@ test "returns 503 when healthcheck enabled and health is false", %{conn: conn} d response = conn |> get("/api/pleroma/healthcheck") - |> json_response(503) + |> json_response_and_validate_schema(503) assert %{ "active" => _, @@ -155,8 +164,8 @@ test "returns 503 when healthcheck enabled and health is false", %{conn: conn} d test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do response = conn - |> post("/api/pleroma/disable_account", %{"password" => "test"}) - |> json_response(:ok) + |> post("/api/pleroma/disable_account?password=test") + |> json_response_and_validate_schema(:ok) assert response == %{"status" => "success"} ObanHelpers.perform_all() @@ -171,8 +180,8 @@ test "with valid permissions and invalid password, it returns an error", %{conn: response = conn - |> post("/api/pleroma/disable_account", %{"password" => "test1"}) - |> json_response(:ok) + |> post("/api/pleroma/disable_account?password=test1") + |> json_response_and_validate_schema(:ok) assert response == %{"error" => "Invalid password."} user = User.get_cached_by_id(user.id) @@ -252,54 +261,61 @@ test "without permissions", %{conn: conn} do conn = conn |> assign(:token, nil) - |> post("/api/pleroma/change_email") + |> post( + "/api/pleroma/change_email?#{ + URI.encode_query(%{password: "hi", email: "test@test.com"}) + }" + ) - assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: write:accounts." + } end test "with proper permissions and invalid password", %{conn: conn} do conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "hi", - "email" => "test@test.com" - }) + post( + conn, + "/api/pleroma/change_email?#{ + URI.encode_query(%{password: "hi", email: "test@test.com"}) + }" + ) - assert json_response(conn, 200) == %{"error" => "Invalid password."} + assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."} end test "with proper permissions, valid password and invalid email", %{ conn: conn } do conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "foobar" - }) + post( + conn, + "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: "foobar"})}" + ) - assert json_response(conn, 200) == %{"error" => "Email has invalid format."} + assert json_response_and_validate_schema(conn, 200) == %{ + "error" => "Email has invalid format." + } end test "with proper permissions, valid password and no email", %{ conn: conn } do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test" - }) + conn = post(conn, "/api/pleroma/change_email?#{URI.encode_query(%{password: "test"})}") - assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + assert %{"error" => "Missing field: email."} = json_response_and_validate_schema(conn, 400) end test "with proper permissions, valid password and blank email", %{ conn: conn } do conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "" - }) + post( + conn, + "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: ""})}" + ) - assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + assert json_response_and_validate_schema(conn, 200) == %{"error" => "Email can't be blank."} end test "with proper permissions, valid password and non unique email", %{ @@ -308,24 +324,28 @@ test "with proper permissions, valid password and non unique email", %{ user = insert(:user) conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => user.email - }) + post( + conn, + "/api/pleroma/change_email?#{URI.encode_query(%{password: "test", email: user.email})}" + ) - assert json_response(conn, 200) == %{"error" => "Email has already been taken."} + assert json_response_and_validate_schema(conn, 200) == %{ + "error" => "Email has already been taken." + } end test "with proper permissions, valid password and valid email", %{ conn: conn } do conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "cofe@foobar.com" - }) + post( + conn, + "/api/pleroma/change_email?#{ + URI.encode_query(%{password: "test", email: "cofe@foobar.com"}) + }" + ) - assert json_response(conn, 200) == %{"status" => "success"} + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} end end @@ -336,20 +356,35 @@ test "without permissions", %{conn: conn} do conn = conn |> assign(:token, nil) - |> post("/api/pleroma/change_password") + |> post( + "/api/pleroma/change_password?#{ + URI.encode_query(%{ + password: "hi", + new_password: "newpass", + new_password_confirmation: "newpass" + }) + }" + ) - assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + assert json_response_and_validate_schema(conn, 403) == %{ + "error" => "Insufficient permissions: write:accounts." + } end test "with proper permissions and invalid password", %{conn: conn} do conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "hi", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) + post( + conn, + "/api/pleroma/change_password?#{ + URI.encode_query(%{ + password: "hi", + new_password: "newpass", + new_password_confirmation: "newpass" + }) + }" + ) - assert json_response(conn, 200) == %{"error" => "Invalid password."} + assert json_response_and_validate_schema(conn, 200) == %{"error" => "Invalid password."} end test "with proper permissions, valid password and new password and confirmation not matching", @@ -357,13 +392,18 @@ test "with proper permissions, valid password and new password and confirmation conn: conn } do conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "notnewpass" - }) + post( + conn, + "/api/pleroma/change_password?#{ + URI.encode_query(%{ + password: "test", + new_password: "newpass", + new_password_confirmation: "notnewpass" + }) + }" + ) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "error" => "New password does not match confirmation." } end @@ -372,13 +412,14 @@ test "with proper permissions, valid password and invalid new password", %{ conn: conn } do conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "", - "new_password_confirmation" => "" - }) + post( + conn, + "/api/pleroma/change_password?#{ + URI.encode_query(%{password: "test", new_password: "", new_password_confirmation: ""}) + }" + ) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "error" => "New password can't be blank." } end @@ -388,13 +429,18 @@ test "with proper permissions, valid password and matching new password and conf user: user } do conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) + post( + conn, + "/api/pleroma/change_password?#{ + URI.encode_query(%{ + password: "test", + new_password: "newpass", + new_password_confirmation: "newpass" + }) + }" + ) - assert json_response(conn, 200) == %{"status" => "success"} + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} fetched_user = User.get_cached_by_id(user.id) assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true end @@ -409,7 +455,7 @@ test "without permissions", %{conn: conn} do |> assign(:token, nil) |> post("/api/pleroma/delete_account") - assert json_response(conn, 403) == + assert json_response_and_validate_schema(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} end @@ -417,14 +463,16 @@ test "with proper permissions and wrong or missing password", %{conn: conn} do for params <- [%{"password" => "hi"}, %{}] do ret_conn = post(conn, "/api/pleroma/delete_account", params) - assert json_response(ret_conn, 200) == %{"error" => "Invalid password."} + assert json_response_and_validate_schema(ret_conn, 200) == %{ + "error" => "Invalid password." + } end end test "with proper permissions and valid password", %{conn: conn, user: user} do - conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) + conn = post(conn, "/api/pleroma/delete_account?password=test") ObanHelpers.perform_all() - assert json_response(conn, 200) == %{"status" => "success"} + assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"} user = User.get_by_id(user.id) refute user.is_active From c6dcd863e28531e0d21ee64a8387bd27c2c0ed31 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 16 Apr 2021 09:59:50 +0000 Subject: [PATCH 11/15] Apply rinpatch's suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 2 +- 1 file changed, 1 insertion(+), 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 62c9826f6..decb6572a 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -59,7 +59,7 @@ def frontend_configurations_operation do def change_password_operation do %Operation{ - tags: ["Accounts"], + tags: ["Account credentials"], summary: "Change account password", security: [%{"oAuth" => ["write:accounts"]}], operationId: "UtilController.change_password", From 30b1d5093808974310a52917e6ab85d528683fae Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Tue, 20 Apr 2021 21:06:32 +0000 Subject: [PATCH 12/15] Apply lanodan's suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 2 +- 1 file changed, 1 insertion(+), 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 decb6572a..6ddc93a92 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -88,7 +88,7 @@ def change_password_operation do def change_email_operation do %Operation{ - tags: ["Accounts"], + tags: ["Account credentials"], summary: "Change account email", security: [%{"oAuth" => ["write:accounts"]}], operationId: "UtilController.change_email", From e104829c2f5b3eae9133ea1a6a81d138c3a8e314 Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Tue, 20 Apr 2021 21:06:39 +0000 Subject: [PATCH 13/15] Apply lanodan's suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 2 +- 1 file changed, 1 insertion(+), 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 6ddc93a92..dbed1b518 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -143,7 +143,7 @@ def update_notificaton_settings_operation do def disable_account_operation do %Operation{ - tags: ["Accounts"], + tags: ["Account credentials"], summary: "Disable Account", security: [%{"oAuth" => ["write:accounts"]}], operationId: "UtilController.disable_account", From 42185d87504ea595138e8e3f5bf9ce6840edd2f1 Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Tue, 20 Apr 2021 21:06:45 +0000 Subject: [PATCH 14/15] Apply lanodan's suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 2 +- 1 file changed, 1 insertion(+), 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 dbed1b518..6e6d330de 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -163,7 +163,7 @@ def disable_account_operation do def delete_account_operation do %Operation{ - tags: ["Accounts"], + tags: ["Account credentials"], summary: "Delete Account", security: [%{"oAuth" => ["write:accounts"]}], operationId: "UtilController.delete_account", From f9bedf5597dd00ce4f429a4077e7bb4473c97410 Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Tue, 20 Apr 2021 21:08:31 +0000 Subject: [PATCH 15/15] Apply lanodan's suggestion(s) to 1 file(s) --- lib/pleroma/web/api_spec/operations/twitter_util_operation.ex | 2 +- 1 file changed, 1 insertion(+), 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 6e6d330de..0cafbc719 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -195,7 +195,7 @@ def captcha_operation do def healthcheck_operation do %Operation{ tags: ["Accounts"], - summary: "Disable Account", + summary: "Quick status check on the instance", security: [%{"oAuth" => ["write:accounts"]}], operationId: "UtilController.healthcheck", parameters: [],