From 6519732045596b1f0b0e83c365db516afba913d9 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 25 Aug 2021 21:01:04 -0600 Subject: [PATCH 01/14] GET /api/v1/apps endpoint --- .../web/api_spec/operations/app_operation.ex | 39 +++++++++++++++++++ .../controllers/app_controller.ex | 10 +++++ .../web/mastodon_api/views/app_view.ex | 4 ++ lib/pleroma/web/o_auth/app.ex | 9 +++++ lib/pleroma/web/router.ex | 2 + .../20210818023112_add_user_id_to_apps.exs | 9 +++++ 6 files changed, 73 insertions(+) create mode 100644 priv/repo/migrations/20210818023112_add_user_id_to_apps.exs diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index dfb1c7170..72032a4e0 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -13,6 +13,19 @@ def open_api_operation(action) do apply(__MODULE__, operation, []) end + @spec index_operation() :: Operation.t() + def index_operation do + %Operation{ + tags: ["Applications"], + summary: "List applications", + description: "List the OAuth applications for the current user", + operationId: "AppController.index", + responses: %{ + 200 => Operation.response("App", "application/json", index_response()), + } + } + end + @spec create_operation() :: Operation.t() def create_operation do %Operation{ @@ -145,4 +158,30 @@ defp create_response do } } end + + defp index_response do + %Schema{ + title: "AppIndexResponse", + description: "Response schema for GET /api/v1/apps", + type: :object, + properties: [%{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + }], + example: [%{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + }] + } + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a95cc52fd..38073c29a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -14,17 +14,27 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) + plug(:skip_plug, OAuthScopesPlug when action in [:index]) + plug(Pleroma.Web.ApiSpec.CastAndValidate) @local_mastodon_name "Mastodon-Local" defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation + @doc "GET /api/v1/apps" + def index(%{assigns: %{user: user}} = conn, _params) do + with apps <- App.get_user_apps(user) do + render(conn, "index.json", %{apps: apps}) + end + end + @doc "POST /api/v1/apps" def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index c406b5a27..450943aee 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -15,6 +15,10 @@ def render("index.json", %{apps: apps, count: count, page_size: page_size, admin } end + def render("index.json", %{apps: apps}) do + render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") + end + def render("show.json", %{admin: true, app: %App{} = app} = assigns) do "show.json" |> render(Map.delete(assigns, :admin)) diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 382750010..94b0e41f0 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do import Ecto.Changeset import Ecto.Query alias Pleroma.Repo + alias Pleroma.User @type t :: %__MODULE__{} @@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do field(:client_secret, :string) field(:trusted, :boolean, default: false) + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all) has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all) @@ -129,6 +132,12 @@ def search(params) do {:ok, Repo.all(query), count} end + @spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()} + def get_user_apps(%User{id: user_id}) do + from(a in __MODULE__, where: a.user_id == ^user_id) + |> Repo.all() + end + @spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} def destroy(id) do with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 74ee23c06..904439564 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -444,6 +444,8 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) + get("/apps", AppController, :index) + get("/accounts/verify_credentials", AccountController, :verify_credentials) patch("/accounts/update_credentials", AccountController, :update_credentials) diff --git a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs new file mode 100644 index 000000000..39e7fbef5 --- /dev/null +++ b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddUserIdToApps do + use Ecto.Migration + + def change do + alter table(:apps) do + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) + end + end +end From ba6914f90a3e39dd75e7775fd37cfbb6ad3d2f3b Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 26 Aug 2021 11:11:37 -0600 Subject: [PATCH 02/14] Fix formatting in app_operation.ex --- .../web/api_spec/operations/app_operation.ex | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 72032a4e0..c2221ac98 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -21,7 +21,7 @@ def index_operation do description: "List the OAuth applications for the current user", operationId: "AppController.index", responses: %{ - 200 => Operation.response("App", "application/json", index_response()), + 200 => Operation.response("App", "application/json", index_response()) } } end @@ -164,24 +164,28 @@ defp index_response do title: "AppIndexResponse", description: "Response schema for GET /api/v1/apps", type: :object, - properties: [%{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - }], - example: [%{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - }] + properties: [ + %{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + } + ], + example: [ + %{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + } + ] } end end From baa8196fc910cfdbaefd6059bdb1a8445d83f563 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 26 Aug 2021 11:55:43 -0600 Subject: [PATCH 03/14] Fix API spec, add app schema --- .../web/api_spec/operations/app_operation.ex | 33 +++---------------- lib/pleroma/web/api_spec/schemas/app.ex | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/app.ex diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index c2221ac98..71d7b9ee8 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers + alias Pleroma.Web.ApiSpec.Schemas.App @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -21,7 +22,7 @@ def index_operation do description: "List the OAuth applications for the current user", operationId: "AppController.index", responses: %{ - 200 => Operation.response("App", "application/json", index_response()) + 200 => Operation.response("Array of App", "application/json", array_of_apps()) } } end @@ -159,33 +160,7 @@ defp create_response do } end - defp index_response do - %Schema{ - title: "AppIndexResponse", - description: "Response schema for GET /api/v1/apps", - type: :object, - properties: [ - %{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - } - ], - example: [ - %{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - } - ] - } + defp array_of_apps do + %Schema{type: :array, items: App, example: [App.schema().example]} end end diff --git a/lib/pleroma/web/api_spec/schemas/app.ex b/lib/pleroma/web/api_spec/schemas/app.ex new file mode 100644 index 000000000..c3d1af3be --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/app.ex @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.App do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "App", + description: "Response schema for an app", + type: :object, + properties: %{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + }, + example: %{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + } + }) +end From eab6291094314846425339ec51fffbc94cab5501 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 11:13:25 -0600 Subject: [PATCH 04/14] Require follow and read OAuth scopes for GET /api/v1/apps --- .../web/api_spec/operations/app_operation.ex | 26 ++----------------- .../controllers/app_controller.ex | 2 +- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 71d7b9ee8..217609b01 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -36,7 +36,7 @@ def create_operation do operationId: "AppController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("App", "application/json", create_response()), + 200 => create_response(), 422 => Operation.response( "Unprocessable Entity", @@ -135,29 +135,7 @@ defp create_request do end defp create_response do - %Schema{ - title: "AppCreateResponse", - description: "Response schema for an app", - type: :object, - properties: %{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - }, - example: %{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - } - } + Operation.response("App", "application/json", App) end defp array_of_apps do diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 38073c29a..e44c4340e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do plug(:skip_auth when action in [:create, :verify_credentials]) - plug(:skip_plug, OAuthScopesPlug when action in [:index]) + plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) plug(Pleroma.Web.ApiSpec.CastAndValidate) From a14e1c0003285adce3c995f1b19a02179a556fd0 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 18:02:36 -0600 Subject: [PATCH 05/14] Move GET /api/v1/apps to GET /api/v1/pleroma/apps --- .../web/api_spec/operations/app_operation.ex | 17 ---------- .../operations/pleroma_app_operation.ex | 31 +++++++++++++++++++ .../controllers/app_controller.ex | 10 ------ .../web/mastodon_api/views/app_view.ex | 4 --- .../pleroma_api/controllers/app_controller.ex | 23 ++++++++++++++ lib/pleroma/web/pleroma_api/views/app_view.ex | 11 +++++++ lib/pleroma/web/router.ex | 3 +- 7 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex create mode 100644 lib/pleroma/web/pleroma_api/controllers/app_controller.ex create mode 100644 lib/pleroma/web/pleroma_api/views/app_view.ex diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 217609b01..5e72c4824 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -14,19 +14,6 @@ def open_api_operation(action) do apply(__MODULE__, operation, []) end - @spec index_operation() :: Operation.t() - def index_operation do - %Operation{ - tags: ["Applications"], - summary: "List applications", - description: "List the OAuth applications for the current user", - operationId: "AppController.index", - responses: %{ - 200 => Operation.response("Array of App", "application/json", array_of_apps()) - } - } - end - @spec create_operation() :: Operation.t() def create_operation do %Operation{ @@ -137,8 +124,4 @@ defp create_request do defp create_response do Operation.response("App", "application/json", App) end - - defp array_of_apps do - %Schema{type: :array, items: App, example: [App.schema().example]} - end end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex new file mode 100644 index 000000000..efaf81af0 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.App + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + @spec index_operation() :: Operation.t() + def index_operation do + %Operation{ + tags: ["Applications"], + summary: "List applications", + description: "List the OAuth applications for the current user", + operationId: "AppController.index", + responses: %{ + 200 => Operation.response("Array of App", "application/json", array_of_apps()) + } + } + end + + defp array_of_apps do + %Schema{type: :array, items: App, example: [App.schema().example]} + end +end \ No newline at end of file diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index e44c4340e..a95cc52fd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -14,27 +14,17 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) - plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) - plug(Pleroma.Web.ApiSpec.CastAndValidate) @local_mastodon_name "Mastodon-Local" defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation - @doc "GET /api/v1/apps" - def index(%{assigns: %{user: user}} = conn, _params) do - with apps <- App.get_user_apps(user) do - render(conn, "index.json", %{apps: apps}) - end - end - @doc "POST /api/v1/apps" def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index 450943aee..c406b5a27 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -15,10 +15,6 @@ def render("index.json", %{apps: apps, count: count, page_size: page_size, admin } end - def render("index.json", %{apps: apps}) do - render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") - end - def render("show.json", %{admin: true, app: %App{} = app} = assigns) do "show.json" |> render(Map.delete(assigns, :admin)) diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex new file mode 100644 index 000000000..6d46d917c --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppController do + use Pleroma.Web, :controller + + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation + + @doc "GET /api/v1/pleroma/apps" + def index(%{assigns: %{user: user}} = conn, _params) do + with apps <- App.get_user_apps(user) do + render(conn, "index.json", %{apps: apps}) + end + end +end \ No newline at end of file diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex new file mode 100644 index 000000000..7dd560f8f --- /dev/null +++ b/lib/pleroma/web/pleroma_api/views/app_view.ex @@ -0,0 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppView do + use Pleroma.Web, :view + + def render("index.json", %{apps: apps}) do + render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") + end +end \ No newline at end of file diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 904439564..2dba21978 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -372,6 +372,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) + get("/apps", AppController, :index) get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index) get("/statuses/:id/reactions", EmojiReactionController, :index) end @@ -444,8 +445,6 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - get("/apps", AppController, :index) - get("/accounts/verify_credentials", AccountController, :verify_credentials) patch("/accounts/update_credentials", AccountController, :update_credentials) From d02cf7b0cd550bc182e7307b90f077e159b5637f Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 18:17:09 -0600 Subject: [PATCH 06/14] Fix lint --- lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex | 2 +- lib/pleroma/web/pleroma_api/controllers/app_controller.ex | 2 +- lib/pleroma/web/pleroma_api/views/app_view.ex | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex index efaf81af0..582a169ee 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex @@ -28,4 +28,4 @@ def index_operation do defp array_of_apps do %Schema{type: :array, items: App, example: [App.schema().example]} end -end \ No newline at end of file +end diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex index 6d46d917c..d857f424f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex @@ -20,4 +20,4 @@ def index(%{assigns: %{user: user}} = conn, _params) do render(conn, "index.json", %{apps: apps}) end end -end \ No newline at end of file +end diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex index 7dd560f8f..6b5d838f5 100644 --- a/lib/pleroma/web/pleroma_api/views/app_view.ex +++ b/lib/pleroma/web/pleroma_api/views/app_view.ex @@ -8,4 +8,4 @@ defmodule Pleroma.Web.PleromaAPI.AppView do def render("index.json", %{apps: apps}) do render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") end -end \ No newline at end of file +end From 33f063204edb63344628bdfa72ff11f81ded62a9 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sat, 28 Aug 2021 23:18:12 -0600 Subject: [PATCH 07/14] Add unit test for Pleroma API app controller --- .../controllers/app_controller.ex | 14 ++++- lib/pleroma/web/o_auth/app.ex | 2 +- .../controllers/app_controller_test.exs | 53 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 test/pleroma/web/pleroma_api/controllers/app_controller_test.exs diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a95cc52fd..466508137 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -10,11 +10,15 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Maps + alias Pleroma.User alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + require Logger + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) @@ -26,13 +30,21 @@ defmodule Pleroma.Web.MastodonAPI.AppController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation @doc "POST /api/v1/apps" - def create(%{body_params: params} = conn, _params) do + def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) + user_id = + with %User{id: id} <- user do + id + else + _ -> nil + end + app_attrs = params |> Map.take([:client_name, :redirect_uris, :website]) |> Map.put(:scopes, scopes) + |> Maps.put_if_present(:user_id, user_id) with cs <- App.register_changeset(%App{}, app_attrs), false <- cs.changes[:client_name] == @local_mastodon_name, diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 94b0e41f0..dacfbadc8 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do @spec changeset(t(), map()) :: Ecto.Changeset.t() def changeset(struct, params) do - cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted]) + cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id]) end @spec register_changeset(t(), map()) :: Ecto.Changeset.t() diff --git a/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs new file mode 100644 index 000000000..5e24e18a8 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppControllerTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Push + + import Pleroma.Factory + + test "apps", %{conn: conn} do + user = insert(:user) + app_attrs = build(:oauth_app) + + creation = + conn + |> put_req_header("content-type", "application/json") + |> assign(:user, user) + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = App.get_user_apps(user) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(creation, 200) + + response = + conn + |> put_req_header("content-type", "application/json") + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read", "follow"])) + |> get("/api/v1/pleroma/apps") + |> json_response_and_validate_schema(200) + + [apps] = response + + assert length(response) == 1 + assert apps["client_id"] == app.client_id + end +end From 2e59cdd80f3e3d14c59aeba1fde2f8f9b8305e1f Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 29 Aug 2021 07:22:03 -0600 Subject: [PATCH 08/14] Fix aliases sorting --- lib/pleroma/web/mastodon_api/controllers/app_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 466508137..d2a35dce2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -11,8 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller alias Pleroma.Maps - alias Pleroma.User alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token From 3117c6099733207b7f2a777f8cb8b5b3b839ebe8 Mon Sep 17 00:00:00 2001 From: Sean King Date: Sun, 29 Aug 2021 07:25:54 -0600 Subject: [PATCH 09/14] Make suggested change for create_response --- lib/pleroma/web/api_spec/operations/app_operation.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 5e72c4824..2284ac127 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -23,7 +23,7 @@ def create_operation do operationId: "AppController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => create_response(), + 200 => Operation.response("App", "application/json", App), 422 => Operation.response( "Unprocessable Entity", @@ -120,8 +120,4 @@ defp create_request do } } end - - defp create_response do - Operation.response("App", "application/json", App) - end end From fa35e24a5ec70ecd92e9e31d1e13da44b9e27b6d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:05:35 -0600 Subject: [PATCH 10/14] Apps: add user_id index --- priv/repo/migrations/20210818023112_add_user_id_to_apps.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs index 39e7fbef5..88a6bce00 100644 --- a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs +++ b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs @@ -5,5 +5,7 @@ def change do alter table(:apps) do add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) end + + create_if_not_exists(index(:apps, [:user_id])) end end From 2e4a1c56c36fcd4b9ef34bd3a771abfe21cc71d5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:14:15 -0600 Subject: [PATCH 11/14] AppController: test creating with and without a user --- .../controllers/app_controller.ex | 13 ++++----- .../controllers/app_controller_test.exs | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 079382b17..ef7331bf3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -28,15 +28,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation @doc "POST /api/v1/apps" - def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do + def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) - - user_id = - with %User{id: id} <- user do - id - else - _ -> nil - end + user_id = get_user_id(conn) app_attrs = params @@ -50,6 +44,9 @@ def create(%{assigns: %{user: user}, body_params: params} = conn, _params) do end end + defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id + defp get_user_id(_conn), do: nil + @doc """ GET /api/v1/apps/verify_credentials Gets compact non-secret representation of the app. Supports app tokens and user tokens. diff --git a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs index 76d81b942..bfbb7f32d 100644 --- a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs @@ -35,6 +35,33 @@ test "apps/verify_credentials", %{conn: conn} do end test "creates an oauth app", %{conn: conn} do + app_attrs = build(:oauth_app) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = Repo.all(App) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(conn, 200) + assert app.user_id == nil + end + + test "creates an oauth app with a user", %{conn: conn} do user = insert(:user) app_attrs = build(:oauth_app) @@ -60,5 +87,6 @@ test "creates an oauth app", %{conn: conn} do } assert expected == json_response_and_validate_schema(conn, 200) + assert app.user_id == user.id end end From cb2a072e6252b7c3f6473f7cfd1af5c0ec732d7b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:29:03 -0600 Subject: [PATCH 12/14] Apps: add test for get_user_apps/1 --- test/pleroma/web/o_auth/app_test.exs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs index fc2f0d940..a5223b0a5 100644 --- a/test/pleroma/web/o_auth/app_test.exs +++ b/test/pleroma/web/o_auth/app_test.exs @@ -41,4 +41,16 @@ test "has unique client_id" do assert error.type == :unique end end + + test "get_user_apps/1" do + user = insert(:user) + + apps = [ + insert(:oauth_app, user_id: user.id), + insert(:oauth_app, user_id: user.id), + insert(:oauth_app, user_id: user.id) + ] + + assert App.get_user_apps(user) == apps + end end From 7704a722c06c9658d4037167dc5b6f01a4582b14 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:30:16 -0600 Subject: [PATCH 13/14] AppController: remove unnecessary `require Logger` --- lib/pleroma/web/mastodon_api/controllers/app_controller.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index ef7331bf3..8d18140ad 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.AppController do alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token - require Logger - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:skip_auth when action in [:create, :verify_credentials]) From 5c80d4087df2f6a8436af87ad109eb9e3bd4e3c1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 27 Dec 2021 18:52:34 -0600 Subject: [PATCH 14/14] PleromaAPI.AppView: add test --- .../web/pleroma_api/views/app_view_test.exs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/pleroma/web/pleroma_api/views/app_view_test.exs diff --git a/test/pleroma/web/pleroma_api/views/app_view_test.exs b/test/pleroma/web/pleroma_api/views/app_view_test.exs new file mode 100644 index 000000000..f0aee6987 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/app_view_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppViewTest do + use Pleroma.DataCase, async: true + alias Pleroma.Web.PleromaAPI.AppView + import Pleroma.Factory + + test "index.json" do + apps = [ + insert(:oauth_app), + insert(:oauth_app), + insert(:oauth_app) + ] + + results = AppView.render("index.json", %{apps: apps}) + + assert [%{client_id: _, client_secret: _}, _, _] = results + end +end