OpenAPI: TwitterAPI Util Controller

This commit is contained in:
Haelwenn (lanodan) Monnier 2021-02-24 23:40:33 +01:00
parent 65cd9cb638
commit 55bdfb075c
No known key found for this signature in database
GPG key ID: D5B7A8E43C997DEE
3 changed files with 360 additions and 87 deletions

View file

@ -0,0 +1,219 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# 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

View file

@ -15,6 +15,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe)
plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe) plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe)
plug( 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 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
with %User{} = user <- User.get_cached_by_nickname(nick), 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
end end
def change_password(%{assigns: %{user: user}} = conn, params) do def change_password(%{assigns: %{user: user}} = conn, %{
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do password: password,
new_password: new_password,
new_password_confirmation: new_password_confirmation
}) do
case CommonAPI.Utils.confirm_current_password(user, password) do
{:ok, user} -> {:ok, user} ->
with {:ok, _user} <- with {:ok, _user} <-
User.reset_password(user, %{ User.reset_password(user, %{
password: params["new_password"], password: new_password,
password_confirmation: params["new_password_confirmation"] password_confirmation: new_password_confirmation
}) do }) do
json(conn, %{status: "success"}) json(conn, %{status: "success"})
else else
@ -102,10 +108,10 @@ def change_password(%{assigns: %{user: user}} = conn, params) do
end end
end end
def change_email(%{assigns: %{user: user}} = conn, params) do def change_email(%{assigns: %{user: user}} = conn, %{password: password, email: email}) do
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do case CommonAPI.Utils.confirm_current_password(user, password) do
{:ok, user} -> {: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"}) json(conn, %{status: "success"})
else else
{:error, changeset} -> {:error, changeset} ->
@ -122,7 +128,7 @@ def change_email(%{assigns: %{user: user}} = conn, params) do
end end
def delete_account(%{assigns: %{user: user}} = conn, params) do def delete_account(%{assigns: %{user: user}} = conn, params) do
password = params["password"] || "" password = params[:password] || ""
case CommonAPI.Utils.confirm_current_password(user, password) do case CommonAPI.Utils.confirm_current_password(user, password) do
{:ok, user} -> {:ok, user} ->
@ -135,7 +141,7 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do
end end
def disable_account(%{assigns: %{user: user}} = conn, params) do 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} -> {:ok, user} ->
User.set_activation_async(user, false) User.set_activation_async(user, false)
json(conn, %{status: "success"}) json(conn, %{status: "success"})

View file

@ -25,11 +25,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
test "it updates notification settings", %{user: user, conn: conn} do test "it updates notification settings", %{user: user, conn: conn} do
conn conn
|> put("/api/pleroma/notification_settings", %{ |> put(
"block_from_strangers" => true, "/api/pleroma/notification_settings?#{
"bar" => 1 URI.encode_query(%{
block_from_strangers: true
}) })
|> json_response(:ok) }"
)
|> json_response_and_validate_schema(:ok)
user = refresh_record(user) 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 test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do
conn conn
|> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) |> put(
|> json_response(:ok) "/api/pleroma/notification_settings?#{
URI.encode_query(%{
hide_notification_contents: 1
})
}"
)
|> json_response_and_validate_schema(:ok)
user = refresh_record(user) user = refresh_record(user)
@ -70,7 +79,7 @@ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} d
response = response =
conn conn
|> get("/api/pleroma/frontend_configurations") |> get("/api/pleroma/frontend_configurations")
|> json_response(:ok) |> json_response_and_validate_schema(:ok)
assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!() assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
end end
@ -81,7 +90,7 @@ test "returns json with custom emoji with tags", %{conn: conn} do
emoji = emoji =
conn conn
|> get("/api/pleroma/emoji") |> get("/api/pleroma/emoji")
|> json_response(200) |> json_response_and_validate_schema(200)
assert Enum.all?(emoji, fn assert Enum.all?(emoji, fn
{_key, {_key,
@ -103,7 +112,7 @@ test "returns 503 when healthcheck disabled", %{conn: conn} do
response = response =
conn conn
|> get("/api/pleroma/healthcheck") |> get("/api/pleroma/healthcheck")
|> json_response(503) |> json_response_and_validate_schema(503)
assert response == %{} assert response == %{}
end end
@ -116,7 +125,7 @@ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do
response = response =
conn conn
|> get("/api/pleroma/healthcheck") |> get("/api/pleroma/healthcheck")
|> json_response(200) |> json_response_and_validate_schema(200)
assert %{ assert %{
"active" => _, "active" => _,
@ -136,7 +145,7 @@ test "returns 503 when healthcheck enabled and health is false", %{conn: conn} d
response = response =
conn conn
|> get("/api/pleroma/healthcheck") |> get("/api/pleroma/healthcheck")
|> json_response(503) |> json_response_and_validate_schema(503)
assert %{ assert %{
"active" => _, "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 test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do
response = response =
conn conn
|> post("/api/pleroma/disable_account", %{"password" => "test"}) |> post("/api/pleroma/disable_account?password=test")
|> json_response(:ok) |> json_response_and_validate_schema(:ok)
assert response == %{"status" => "success"} assert response == %{"status" => "success"}
ObanHelpers.perform_all() ObanHelpers.perform_all()
@ -171,8 +180,8 @@ test "with valid permissions and invalid password, it returns an error", %{conn:
response = response =
conn conn
|> post("/api/pleroma/disable_account", %{"password" => "test1"}) |> post("/api/pleroma/disable_account?password=test1")
|> json_response(:ok) |> json_response_and_validate_schema(:ok)
assert response == %{"error" => "Invalid password."} assert response == %{"error" => "Invalid password."}
user = User.get_cached_by_id(user.id) user = User.get_cached_by_id(user.id)
@ -252,54 +261,61 @@ test "without permissions", %{conn: conn} do
conn = conn =
conn conn
|> assign(:token, nil) |> 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 end
test "with proper permissions and invalid password", %{conn: conn} do test "with proper permissions and invalid password", %{conn: conn} do
conn = conn =
post(conn, "/api/pleroma/change_email", %{ post(
"password" => "hi", conn,
"email" => "test@test.com" "/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 end
test "with proper permissions, valid password and invalid email", %{ test "with proper permissions, valid password and invalid email", %{
conn: conn conn: conn
} do } do
conn = conn =
post(conn, "/api/pleroma/change_email", %{ post(
"password" => "test", conn,
"email" => "foobar" "/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 end
test "with proper permissions, valid password and no email", %{ test "with proper permissions, valid password and no email", %{
conn: conn conn: conn
} do } do
conn = conn = post(conn, "/api/pleroma/change_email?#{URI.encode_query(%{password: "test"})}")
post(conn, "/api/pleroma/change_email", %{
"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 end
test "with proper permissions, valid password and blank email", %{ test "with proper permissions, valid password and blank email", %{
conn: conn conn: conn
} do } do
conn = conn =
post(conn, "/api/pleroma/change_email", %{ post(
"password" => "test", conn,
"email" => "" "/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 end
test "with proper permissions, valid password and non unique email", %{ 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) user = insert(:user)
conn = conn =
post(conn, "/api/pleroma/change_email", %{ post(
"password" => "test", conn,
"email" => user.email "/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 end
test "with proper permissions, valid password and valid email", %{ test "with proper permissions, valid password and valid email", %{
conn: conn conn: conn
} do } do
conn = conn =
post(conn, "/api/pleroma/change_email", %{ post(
"password" => "test", conn,
"email" => "cofe@foobar.com" "/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
end end
@ -336,20 +356,35 @@ test "without permissions", %{conn: conn} do
conn = conn =
conn conn
|> assign(:token, nil) |> 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 end
test "with proper permissions and invalid password", %{conn: conn} do test "with proper permissions and invalid password", %{conn: conn} do
conn = conn =
post(conn, "/api/pleroma/change_password", %{ post(
"password" => "hi", conn,
"new_password" => "newpass", "/api/pleroma/change_password?#{
"new_password_confirmation" => "newpass" 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 end
test "with proper permissions, valid password and new password and confirmation not matching", 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 conn: conn
} do } do
conn = conn =
post(conn, "/api/pleroma/change_password", %{ post(
"password" => "test", conn,
"new_password" => "newpass", "/api/pleroma/change_password?#{
"new_password_confirmation" => "notnewpass" 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." "error" => "New password does not match confirmation."
} }
end end
@ -372,13 +412,14 @@ test "with proper permissions, valid password and invalid new password", %{
conn: conn conn: conn
} do } do
conn = conn =
post(conn, "/api/pleroma/change_password", %{ post(
"password" => "test", conn,
"new_password" => "", "/api/pleroma/change_password?#{
"new_password_confirmation" => "" 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." "error" => "New password can't be blank."
} }
end end
@ -388,13 +429,18 @@ test "with proper permissions, valid password and matching new password and conf
user: user user: user
} do } do
conn = conn =
post(conn, "/api/pleroma/change_password", %{ post(
"password" => "test", conn,
"new_password" => "newpass", "/api/pleroma/change_password?#{
"new_password_confirmation" => "newpass" 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) fetched_user = User.get_cached_by_id(user.id)
assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true
end end
@ -409,7 +455,7 @@ test "without permissions", %{conn: conn} do
|> assign(:token, nil) |> assign(:token, nil)
|> post("/api/pleroma/delete_account") |> post("/api/pleroma/delete_account")
assert json_response(conn, 403) == assert json_response_and_validate_schema(conn, 403) ==
%{"error" => "Insufficient permissions: write:accounts."} %{"error" => "Insufficient permissions: write:accounts."}
end end
@ -417,14 +463,16 @@ test "with proper permissions and wrong or missing password", %{conn: conn} do
for params <- [%{"password" => "hi"}, %{}] do for params <- [%{"password" => "hi"}, %{}] do
ret_conn = post(conn, "/api/pleroma/delete_account", params) 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
end end
test "with proper permissions and valid password", %{conn: conn, user: user} do 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() 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) user = User.get_by_id(user.id)
refute user.is_active refute user.is_active