From a6ee6784bc74b311d454112c427f41b1fdec6ce0 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Fri, 28 Feb 2020 11:16:40 +0300 Subject: creating trusted app from adminFE & mix task --- lib/mix/tasks/pleroma/app.ex | 49 +++++++++++++ lib/pleroma/web/admin_api/admin_api_controller.ex | 79 +++++++++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 1 + lib/pleroma/web/mastodon_api/views/app_view.ex | 15 ++++ lib/pleroma/web/oauth/app.ex | 82 +++++++++++++++++++++- lib/pleroma/web/router.ex | 5 ++ lib/pleroma/web/twitter_api/twitter_api.ex | 3 +- 7 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 lib/mix/tasks/pleroma/app.ex (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/app.ex b/lib/mix/tasks/pleroma/app.ex new file mode 100644 index 000000000..463e2449f --- /dev/null +++ b/lib/mix/tasks/pleroma/app.ex @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.App do + @moduledoc File.read!("docs/administration/CLI_tasks/oauth_app.md") + use Mix.Task + + import Mix.Pleroma + + @shortdoc "Creates trusted OAuth App" + + def run(["create" | options]) do + start_pleroma() + + {opts, _} = + OptionParser.parse!(options, + strict: [name: :string, redirect_uri: :string, scopes: :string], + aliases: [n: :name, r: :redirect_uri, s: :scopes] + ) + + scopes = + if opts[:scopes] do + String.split(opts[:scopes], ",") + else + ["read", "write", "follow", "push"] + end + + params = %{ + client_name: opts[:name], + redirect_uris: opts[:redirect_uri], + trusted: true, + scopes: scopes + } + + with {:ok, app} <- Pleroma.Web.OAuth.App.create(params) do + shell_info("#{app.client_name} successfully created:") + shell_info("App client_id: " <> app.client_id) + shell_info("App client_secret: " <> app.client_secret) + else + {:error, changeset} -> + shell_error("Creating failed:") + + Enum.each(Pleroma.Web.OAuth.App.errors(changeset), fn {key, error} -> + shell_error("#{key}: #{error}") + end) + end + end +end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 175260bc2..b03fa7169 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.CommonAPI alias Pleroma.Web.Endpoint + alias Pleroma.Web.MastodonAPI.AppView alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.OAuth.App alias Pleroma.Web.Router require Logger @@ -978,6 +980,83 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do conn |> json("") end + def oauth_app_create(conn, params) do + params = + if params["name"] do + Map.put(params, "client_name", params["name"]) + else + params + end + + result = + case App.create(params) do + {:ok, app} -> + AppView.render("show.json", %{app: app, admin: true}) + + {:error, changeset} -> + App.errors(changeset) + end + + json(conn, result) + end + + def oauth_app_update(conn, params) do + params = + if params["name"] do + Map.put(params, "client_name", params["name"]) + else + params + end + + with {:ok, app} <- App.update(params) do + json(conn, AppView.render("show.json", %{app: app, admin: true})) + else + {:error, changeset} -> + json(conn, App.errors(changeset)) + + nil -> + json_response(conn, :bad_request, "") + end + end + + def oauth_app_list(conn, params) do + {page, page_size} = page_params(params) + + search_params = %{ + client_name: params["name"], + client_id: params["client_id"], + page: page, + page_size: page_size + } + + search_params = + if Map.has_key?(params, "trusted") do + Map.put(search_params, :trusted, params["trusted"]) + else + search_params + end + + with {:ok, apps, count} <- App.search(search_params) do + json( + conn, + AppView.render("index.json", + apps: apps, + count: count, + page_size: page_size, + admin: true + ) + ) + end + end + + def oauth_app_delete(conn, params) do + with {:ok, _app} <- App.destroy(params["id"]) do + json_response(conn, :no_content, "") + else + _ -> json_response(conn, :bad_request, "") + end + end + def stats(conn, _) do count = Stats.get_status_visibility_count() diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 6dbf11ac9..5f8aa2e3e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -92,6 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> Map.put("fullname", params["fullname"] || nickname) |> Map.put("bio", params["bio"] || "") |> Map.put("confirm", params["password"]) + |> Map.put("trusted_app", app.trusted) with :ok <- validate_email_param(params), {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index d934e2107..36071cd25 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do alias Pleroma.Web.OAuth.App + def render("index.json", %{apps: apps, count: count, page_size: page_size, admin: true}) do + %{ + apps: render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json", %{admin: true}), + count: count, + page_size: page_size + } + end + + def render("show.json", %{admin: true, app: %App{} = app} = assigns) do + "show.json" + |> render(Map.delete(assigns, :admin)) + |> Map.put(:trusted, app.trusted) + |> Map.put(:id, app.id) + end + def render("show.json", %{app: %App{} = app}) do %{ id: app.id |> to_string, diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index 01ed326f4..6a6d5f2e2 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.OAuth.App do use Ecto.Schema import Ecto.Changeset + import Ecto.Query alias Pleroma.Repo @type t :: %__MODULE__{} @@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do field(:website, :string) field(:client_id, :string) field(:client_secret, :string) + field(:trusted, :boolean, default: false) + + has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all) + has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all) timestamps() end + @spec changeset(App.t(), map()) :: Ecto.Changeset.t() + def changeset(struct, params) do + cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted]) + end + + @spec register_changeset(App.t(), map()) :: Ecto.Changeset.t() def register_changeset(struct, params \\ %{}) do changeset = struct - |> cast(params, [:client_name, :redirect_uris, :scopes, :website]) + |> changeset(params) |> validate_required([:client_name, :redirect_uris, :scopes]) if changeset.valid? do @@ -41,6 +52,21 @@ defmodule Pleroma.Web.OAuth.App do end end + @spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()} + def create(params) do + with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do + Repo.insert(changeset) + end + end + + @spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()} + def update(params) do + with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]), + changeset <- changeset(app, params) do + Repo.update(changeset) + end + end + @doc """ Gets app by attrs or create new with attrs. And updates the scopes if need. @@ -65,4 +91,58 @@ defmodule Pleroma.Web.OAuth.App do |> change(%{scopes: scopes}) |> Repo.update() end + + @spec search(map()) :: {:ok, [App.t()], non_neg_integer()} + def search(params) do + query = from(a in __MODULE__) + + query = + if params[:client_name] do + from(a in query, where: a.client_name == ^params[:client_name]) + else + query + end + + query = + if params[:client_id] do + from(a in query, where: a.client_id == ^params[:client_id]) + else + query + end + + query = + if Map.has_key?(params, :trusted) do + from(a in query, where: a.trusted == ^params[:trusted]) + else + query + end + + query = + from(u in query, + limit: ^params[:page_size], + offset: ^((params[:page] - 1) * params[:page_size]) + ) + + count = Repo.aggregate(__MODULE__, :count, :id) + + {:ok, Repo.all(query), count} + end + + @spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()} + def destroy(id) do + with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do + Repo.delete(app) + end + end + + @spec errors(Ecto.Changeset.t()) :: map() + def errors(changeset) do + Enum.reduce(changeset.errors, %{}, fn + {:client_name, {error, _}}, acc -> + Map.put(acc, :name, error) + + {key, {error, _}}, acc -> + Map.put(acc, key, error) + end) + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 3f36f6c1a..c37ef59a0 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -203,6 +203,11 @@ defmodule Pleroma.Web.Router do post("/reload_emoji", AdminAPIController, :reload_emoji) get("/stats", AdminAPIController, :stats) + + get("/oauth_app", AdminAPIController, :oauth_app_list) + post("/oauth_app", AdminAPIController, :oauth_app_create) + patch("/oauth_app/:id", AdminAPIController, :oauth_app_update) + delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete) end scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index f9c0994da..7a1ba6936 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do def register_user(params, opts \\ []) do token = params["token"] + trusted_app? = params["trusted_app"] params = %{ nickname: params["nickname"], @@ -29,7 +30,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled]) # true if captcha is disabled or enabled and valid, false otherwise captcha_ok = - if not captcha_enabled do + if trusted_app? || not captcha_enabled do :ok else Pleroma.Captcha.validate( -- cgit v1.2.3 From fc81e5a49c34224e07e85f490a30f92db0835d45 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 6 Apr 2020 10:20:44 +0300 Subject: Enforcement of OAuth scopes check for authenticated API endpoints, :skip_plug plug to mark a plug explicitly skipped (disabled). --- lib/pleroma/plugs/auth_expected_plug.ex | 13 ++++++++ lib/pleroma/plugs/oauth_scopes_plug.ex | 3 ++ lib/pleroma/plugs/plug_helper.ex | 38 ++++++++++++++++++++++ lib/pleroma/web/masto_fe_controller.ex | 2 +- .../mastodon_api/controllers/account_controller.ex | 9 +++-- .../controllers/mastodon_api_controller.ex | 18 +++++++--- .../controllers/suggestion_controller.ex | 9 +++-- lib/pleroma/web/oauth/oauth_controller.ex | 2 ++ .../controllers/pleroma_api_controller.ex | 2 +- lib/pleroma/web/router.ex | 3 +- .../web/twitter_api/twitter_api_controller.ex | 2 ++ lib/pleroma/web/web.ex | 23 +++++++++++++ 12 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 lib/pleroma/plugs/auth_expected_plug.ex create mode 100644 lib/pleroma/plugs/plug_helper.ex (limited to 'lib') diff --git a/lib/pleroma/plugs/auth_expected_plug.ex b/lib/pleroma/plugs/auth_expected_plug.ex new file mode 100644 index 000000000..9e4a4bec8 --- /dev/null +++ b/lib/pleroma/plugs/auth_expected_plug.ex @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.AuthExpectedPlug do + import Plug.Conn + + def init(options), do: options + + def call(conn, _) do + put_private(conn, :auth_expected, true) + end +end diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 38df074ad..b09e1bb4d 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -8,12 +8,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do alias Pleroma.Config alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.PlugHelper @behaviour Plug def init(%{scopes: _} = options), do: options def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do + conn = PlugHelper.append_to_called_plugs(conn, __MODULE__) + op = options[:op] || :| token = assigns[:token] diff --git a/lib/pleroma/plugs/plug_helper.ex b/lib/pleroma/plugs/plug_helper.ex new file mode 100644 index 000000000..4f83e9414 --- /dev/null +++ b/lib/pleroma/plugs/plug_helper.ex @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.PlugHelper do + @moduledoc "Pleroma Plug helper" + + def append_to_called_plugs(conn, plug_module) do + append_to_private_list(conn, :called_plugs, plug_module) + end + + def append_to_skipped_plugs(conn, plug_module) do + append_to_private_list(conn, :skipped_plugs, plug_module) + end + + def plug_called?(conn, plug_module) do + contained_in_private_list?(conn, :called_plugs, plug_module) + end + + def plug_skipped?(conn, plug_module) do + contained_in_private_list?(conn, :skipped_plugs, plug_module) + end + + def plug_called_or_skipped?(conn, plug_module) do + plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module) + end + + defp append_to_private_list(conn, private_variable, value) do + list = conn.private[private_variable] || [] + modified_list = Enum.uniq(list ++ [value]) + Plug.Conn.put_private(conn, private_variable, modified_list) + end + + defp contained_in_private_list?(conn, private_variable, value) do + list = conn.private[private_variable] || [] + value in list + end +end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 43649ad26..557cde328 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastoFEController do when action == :index ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest]) @doc "GET /web/*path" def index(%{assigns: %{user: user, token: token}} = conn, _params) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 21bc3d5a5..bd6853d12 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,10 +15,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI + alias Pleroma.Web.MastodonAPI.MastodonAPIController alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI + plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs) + plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} @@ -369,6 +372,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/endorsements" - def endorsements(conn, params), - do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) + def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params) + + @doc "GET /api/v1/identity_proofs" + def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 14075307d..ac8c18f24 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -3,21 +3,31 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do + @moduledoc """ + Contains stubs for unimplemented Mastodon API endpoints. + + Note: instead of routing directly to this controller's action, + it's preferable to define an action in relevant (non-generic) controller, + set up OAuth rules for it and call this controller's function from it. + """ + use Pleroma.Web, :controller require Logger + plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object]) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - # Stubs for unimplemented mastodon api - # def empty_array(conn, _) do - Logger.debug("Unimplemented, returning an empty array") + Logger.debug("Unimplemented, returning an empty array (list)") json(conn, []) end def empty_object(conn, _) do - Logger.debug("Unimplemented, returning an empty object") + Logger.debug("Unimplemented, returning an empty object (map)") json(conn, %{}) end end diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 0cdc7bd8d..c93a43969 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -5,10 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug + require Logger + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + @doc "GET /api/v1/suggestions" - def index(conn, _) do - json(conn, []) - end + def index(conn, params), + do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 46688db7e..0121cd661 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -27,6 +27,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_flash) plug(RateLimiter, [name: :authentication] when action == :create_authorization) + plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug) + action_fallback(Pleroma.Web.OAuth.FallbackController) @oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob" diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index dae7f0f2f..75f61b675 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug( OAuthScopesPlug, - %{scopes: ["write:conversations"]} when action == :update_conversation + %{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations] ) plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 5a0902739..3d57073d0 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -34,6 +34,7 @@ defmodule Pleroma.Web.Router do pipeline :authenticated_api do plug(:accepts, ["json"]) plug(:fetch_session) + plug(Pleroma.Plugs.AuthExpectedPlug) plug(Pleroma.Plugs.OAuthPlug) plug(Pleroma.Plugs.BasicAuthDecoderPlug) plug(Pleroma.Plugs.UserFetcherPlug) @@ -333,7 +334,7 @@ defmodule Pleroma.Web.Router do get("/accounts/relationships", AccountController, :relationships) get("/accounts/:id/lists", AccountController, :lists) - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + get("/accounts/:id/identity_proofs", AccountController, :identity_proofs) get("/follow_requests", FollowRequestController, :index) get("/blocks", AccountController, :blocks) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 0229aea97..31adc2817 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token]) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) action_fallback(:errors) diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index cf3ac1287..1af29ce78 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -29,11 +29,34 @@ defmodule Pleroma.Web do import Pleroma.Web.Router.Helpers import Pleroma.Web.TranslationHelpers + alias Pleroma.Plugs.PlugHelper + plug(:set_put_layout) defp set_put_layout(conn, _) do put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) end + + # Marks a plug as intentionally skipped + # (states that the plug is not called for a good reason, not by a mistake) + defp skip_plug(conn, plug_module) do + PlugHelper.append_to_skipped_plugs(conn, plug_module) + end + + # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed) + defp action(conn, params) do + if conn.private[:auth_expected] && + not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do + conn + |> render_error( + :forbidden, + "Security violation: OAuth scopes check was neither handled nor explicitly skipped." + ) + |> halt() + else + super(conn, params) + end + end end end -- cgit v1.2.3 From 1a4875adfa8fa8f65f1db7b4ec3cf868b7e3dee7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 7 Apr 2020 21:52:32 +0300 Subject: [#1559] Support for "follow_request" notifications (configurable). (Not currently supported by PleromaFE, thus disabled by default). --- lib/pleroma/activity.ex | 36 ++++++++-- lib/pleroma/notification.ex | 11 +++- lib/pleroma/user.ex | 2 + .../web/mastodon_api/views/notification_view.ex | 3 + lib/pleroma/web/push/impl.ex | 76 ++++++++++++++-------- lib/pleroma/web/push/subscription.ex | 8 +++ 6 files changed, 100 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 5a8329e69..3803d8e50 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -27,17 +27,13 @@ defmodule Pleroma.Activity do # https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19 @mastodon_notification_types %{ "Create" => "mention", - "Follow" => "follow", + "Follow" => ["follow", "follow_request"], "Announce" => "reblog", "Like" => "favourite", "Move" => "move", "EmojiReact" => "pleroma:emoji_reaction" } - @mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types, - into: %{}, - do: {v, k} - schema "activities" do field(:data, :map) field(:local, :boolean, default: true) @@ -291,15 +287,41 @@ defmodule Pleroma.Activity do defp purge_web_resp_cache(nil), do: nil - for {ap_type, type} <- @mastodon_notification_types do + def follow_accepted?( + %Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity + ) do + with %User{} = follower <- Activity.user_actor(activity), + %User{} = followed <- User.get_cached_by_ap_id(followed_ap_id) do + Pleroma.FollowingRelationship.following?(follower, followed) + else + _ -> false + end + end + + def follow_accepted?(_), do: false + + for {ap_type, type} <- @mastodon_notification_types, not is_list(type) do def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}), do: unquote(type) end + def mastodon_notification_type(%Activity{data: %{"type" => "Follow"}} = activity) do + if follow_accepted?(activity) do + "follow" + else + "follow_request" + end + end + def mastodon_notification_type(%Activity{}), do: nil def from_mastodon_notification_type(type) do - Map.get(@mastodon_to_ap_notification_types, type) + with {k, _v} <- + Enum.find(@mastodon_notification_types, fn {_k, v} -> + v == type or (is_list(v) and type in v) + end) do + k + end end def all_by_actor_and_id(actor, status_ids \\ []) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 04ee510b9..73e19bf97 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -284,8 +284,17 @@ defmodule Pleroma.Notification do end end + def create_notifications(%Activity{data: %{"type" => "Follow"}} = activity) do + if Pleroma.Config.get([:notifications, :enable_follow_request_notifications]) || + Activity.follow_accepted?(activity) do + do_create_notifications(activity) + else + {:ok, []} + end + end + def create_notifications(%Activity{data: %{"type" => type}} = activity) - when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do + when type in ["Like", "Announce", "Move", "EmojiReact"] do do_create_notifications(activity) end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 71c8c3a4e..ac2594417 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -699,6 +699,8 @@ defmodule Pleroma.User do def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} + + # "Locked" (self-locked) users demand explicit authorization of follow requests def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do follow(follower, followed, "pending") end diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index ae87d4701..feed47129 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -116,6 +116,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do "follow" -> response + "follow_request" -> + response + "pleroma:emoji_reaction" -> response |> put_status(parent_activity_fn.(), reading_user, render_opts) diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index afa510f08..89d45b2e1 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,6 +16,8 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query + defdelegate mastodon_notification_type(activity), to: Activity + @types ["Create", "Follow", "Announce", "Like", "Move"] @doc "Performs sending notifications for user subscriptions" @@ -24,32 +26,32 @@ defmodule Pleroma.Web.Push.Impl do %{ activity: %{data: %{"type" => activity_type}} = activity, user: %User{id: user_id} - } = notif + } = notification ) when activity_type in @types do - actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) - type = Activity.mastodon_notification_type(notif.activity) + mastodon_type = mastodon_notification_type(notification.activity) gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) avatar_url = User.avatar_url(actor) object = Object.normalize(activity) user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) - for subscription <- fetch_subsriptions(user_id), - get_in(subscription.data, ["alerts", type]) do + for subscription <- fetch_subscriptions(user_id), + Subscription.enabled?(subscription, mastodon_type) do %{ access_token: subscription.token.token, - notification_id: notif.id, - notification_type: type, + notification_id: notification.id, + notification_type: mastodon_type, icon: avatar_url, preferred_locale: "en", pleroma: %{ - activity_id: notif.activity.id, + activity_id: notification.activity.id, direct_conversation_id: direct_conversation_id } } - |> Map.merge(build_content(notif, actor, object)) + |> Map.merge(build_content(notification, actor, object, mastodon_type)) |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end @@ -82,7 +84,7 @@ defmodule Pleroma.Web.Push.Impl do end @doc "Gets user subscriptions" - def fetch_subsriptions(user_id) do + def fetch_subscriptions(user_id) do Subscription |> where(user_id: ^user_id) |> preload(:token) @@ -99,28 +101,36 @@ defmodule Pleroma.Web.Push.Impl do } end + def build_content(notification, actor, object, mastodon_type \\ nil) + def build_content( %{ activity: %{data: %{"directMessage" => true}}, user: %{notification_settings: %{privacy_option: true}} }, actor, - _ + _object, + _mastodon_type ) do %{title: "New Direct Message", body: "@#{actor.nickname}"} end - def build_content(notif, actor, object) do + def build_content(notification, actor, object, mastodon_type) do + mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + %{ - title: format_title(notif), - body: format_body(notif, actor, object) + title: format_title(notification, mastodon_type), + body: format_body(notification, actor, object, mastodon_type) } end + def format_body(activity, actor, object, mastodon_type \\ nil) + def format_body( %{activity: %{data: %{"type" => "Create"}}}, actor, - %{data: %{"content" => content}} + %{data: %{"content" => content}}, + _mastodon_type ) do "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end @@ -128,33 +138,43 @@ defmodule Pleroma.Web.Push.Impl do def format_body( %{activity: %{data: %{"type" => "Announce"}}}, actor, - %{data: %{"content" => content}} + %{data: %{"content" => content}}, + _mastodon_type ) do "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( - %{activity: %{data: %{"type" => type}}}, + %{activity: %{data: %{"type" => type}}} = notification, actor, - _object + _object, + mastodon_type ) when type in ["Follow", "Like"] do - case type do - "Follow" -> "@#{actor.nickname} has followed you" - "Like" -> "@#{actor.nickname} has favorited your post" + mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + + case {type, mastodon_type} do + {"Follow", "follow"} -> "@#{actor.nickname} has followed you" + {"Follow", "follow_request"} -> "@#{actor.nickname} has requested to follow you" + {"Like", _} -> "@#{actor.nickname} has favorited your post" end end - def format_title(%{activity: %{data: %{"directMessage" => true}}}) do + def format_title(activity, mastodon_type \\ nil) + + def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do "New Direct Message" end - def format_title(%{activity: %{data: %{"type" => type}}}) do - case type do - "Create" -> "New Mention" - "Follow" -> "New Follower" - "Announce" -> "New Repeat" - "Like" -> "New Favorite" + def format_title(%{activity: %{data: %{"type" => type}}} = notification, mastodon_type) do + mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + + case {type, mastodon_type} do + {"Create", _} -> "New Mention" + {"Follow", "follow"} -> "New Follower" + {"Follow", "follow_request"} -> "New Follow Request" + {"Announce", _} -> "New Repeat" + {"Like", _} -> "New Favorite" end end end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 5c448d6c9..b99b0c5fb 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -32,6 +32,14 @@ defmodule Pleroma.Web.Push.Subscription do %{"alerts" => alerts} end + def enabled?(subscription, "follow_request") do + enabled?(subscription, "follow") + end + + def enabled?(subscription, alert_type) do + get_in(subscription.data, ["alerts", alert_type]) + end + def create( %User{} = user, %Token{} = token, -- cgit v1.2.3 From ac672a9d6bfdd3cba7692f80a883bd38b0b09a57 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 9 Apr 2020 15:13:37 +0300 Subject: [#1559] Addressed code review requests. --- lib/pleroma/activity.ex | 8 ++++--- .../web/mastodon_api/views/notification_view.ex | 9 +++----- lib/pleroma/web/push/impl.ex | 25 +++++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 3803d8e50..6213d0eb7 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -300,6 +300,8 @@ defmodule Pleroma.Activity do def follow_accepted?(_), do: false + @spec mastodon_notification_type(Activity.t()) :: String.t() | nil + for {ap_type, type} <- @mastodon_notification_types, not is_list(type) do def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}), do: unquote(type) @@ -315,11 +317,11 @@ defmodule Pleroma.Activity do def mastodon_notification_type(%Activity{}), do: nil + @spec from_mastodon_notification_type(String.t()) :: String.t() | nil + @doc "Converts Mastodon notification type to AR activity type" def from_mastodon_notification_type(type) do with {k, _v} <- - Enum.find(@mastodon_notification_types, fn {_k, v} -> - v == type or (is_list(v) and type in v) - end) do + Enum.find(@mastodon_notification_types, fn {_k, v} -> type in List.wrap(v) end) do k end end diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index feed47129..7001fd7b9 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -113,17 +113,14 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do "move" -> put_target(response, activity, reading_user, render_opts) - "follow" -> - response - - "follow_request" -> - response - "pleroma:emoji_reaction" -> response |> put_status(parent_activity_fn.(), reading_user, render_opts) |> put_emoji(activity) + type when type in ["follow", "follow_request"] -> + response + _ -> nil end diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 89d45b2e1..f1740a6e0 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -153,10 +153,10 @@ defmodule Pleroma.Web.Push.Impl do when type in ["Follow", "Like"] do mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) - case {type, mastodon_type} do - {"Follow", "follow"} -> "@#{actor.nickname} has followed you" - {"Follow", "follow_request"} -> "@#{actor.nickname} has requested to follow you" - {"Like", _} -> "@#{actor.nickname} has favorited your post" + case mastodon_type do + "follow" -> "@#{actor.nickname} has followed you" + "follow_request" -> "@#{actor.nickname} has requested to follow you" + "favourite" -> "@#{actor.nickname} has favorited your post" end end @@ -166,15 +166,16 @@ defmodule Pleroma.Web.Push.Impl do "New Direct Message" end - def format_title(%{activity: %{data: %{"type" => type}}} = notification, mastodon_type) do - mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + def format_title(%{activity: activity}, mastodon_type) do + mastodon_type = mastodon_type || mastodon_notification_type(activity) - case {type, mastodon_type} do - {"Create", _} -> "New Mention" - {"Follow", "follow"} -> "New Follower" - {"Follow", "follow_request"} -> "New Follow Request" - {"Announce", _} -> "New Repeat" - {"Like", _} -> "New Favorite" + case mastodon_type do + "mention" -> "New Mention" + "follow" -> "New Follower" + "follow_request" -> "New Follow Request" + "reblog" -> "New Repeat" + "favourite" -> "New Favorite" + type -> "New #{String.capitalize(type || "event")}" end end end -- cgit v1.2.3 From 5628984df4809888746ea005decf3856ca929858 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 1 Apr 2020 01:50:53 +0200 Subject: User: remove source_data use for follower_address and following_address --- lib/pleroma/user.ex | 91 ++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 46 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 71c8c3a4e..d030c7314 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -306,6 +306,7 @@ defmodule Pleroma.User do end end + # Should probably be renamed or removed def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}" def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa @@ -339,6 +340,13 @@ defmodule Pleroma.User do end end + defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params + + defp fix_follower_address(%{nickname: nickname} = params), + do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname})) + + defp fix_follower_address(params), do: params + def remote_user_creation(params) do bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) @@ -348,53 +356,44 @@ defmodule Pleroma.User do |> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:bio, bio_limit) |> truncate_fields_param() + |> fix_follower_address() - changeset = - %User{local: false} - |> cast( - params, - [ - :bio, - :name, - :ap_id, - :nickname, - :avatar, - :ap_enabled, - :source_data, - :banner, - :locked, - :magic_key, - :uri, - :hide_followers, - :hide_follows, - :hide_followers_count, - :hide_follows_count, - :follower_count, - :fields, - :following_count, - :discoverable, - :invisible, - :actor_type, - :also_known_as - ] - ) - |> validate_required([:name, :ap_id]) - |> unique_constraint(:nickname) - |> validate_format(:nickname, @email_regex) - |> validate_length(:bio, max: bio_limit) - |> validate_length(:name, max: name_limit) - |> validate_fields(true) - - case params[:source_data] do - %{"followers" => followers, "following" => following} -> - changeset - |> put_change(:follower_address, followers) - |> put_change(:following_address, following) - - _ -> - followers = ap_followers(%User{nickname: get_field(changeset, :nickname)}) - put_change(changeset, :follower_address, followers) - end + %User{local: false} + |> cast( + params, + [ + :bio, + :name, + :ap_id, + :nickname, + :avatar, + :ap_enabled, + :source_data, + :banner, + :locked, + :magic_key, + :uri, + :follower_address, + :following_address, + :hide_followers, + :hide_follows, + :hide_followers_count, + :hide_follows_count, + :follower_count, + :fields, + :following_count, + :discoverable, + :invisible, + :actor_type, + :also_known_as + ] + ) + |> validate_required([:name, :ap_id]) + |> unique_constraint(:nickname) + |> validate_format(:nickname, @email_regex) + |> validate_length(:bio, max: bio_limit) + |> validate_length(:name, max: name_limit) + |> validate_fields(true) end def update_changeset(struct, params \\ %{}) do -- cgit v1.2.3 From 19eedb3d0424abb235eec1a51457ed0bf3a0e95d Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 1 Apr 2020 06:58:48 +0200 Subject: User: Move public_key from source_data to own field --- lib/pleroma/user.ex | 9 ++++++--- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d030c7314..0adea42ec 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -82,6 +82,7 @@ defmodule Pleroma.User do field(:password, :string, virtual: true) field(:password_confirmation, :string, virtual: true) field(:keys, :string) + field(:public_key, :string) field(:ap_id, :string) field(:avatar, :map) field(:local, :boolean, default: true) @@ -366,6 +367,7 @@ defmodule Pleroma.User do :name, :ap_id, :nickname, + :public_key, :avatar, :ap_enabled, :source_data, @@ -407,6 +409,7 @@ defmodule Pleroma.User do :bio, :name, :avatar, + :public_key, :locked, :no_rich_text, :default_scope, @@ -503,6 +506,7 @@ defmodule Pleroma.User do :name, :follower_address, :following_address, + :public_key, :avatar, :last_refreshed_at, :ap_enabled, @@ -1616,8 +1620,7 @@ defmodule Pleroma.User do |> set_cache() end - # AP style - def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do + def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do key = public_key_pem |> :public_key.pem_decode() @@ -1627,7 +1630,7 @@ defmodule Pleroma.User do {:ok, key} end - def public_key(_), do: {:error, "not found key"} + def public_key(_), do: {:error, "key not found"} def get_public_key_for_ap_id(ap_id) do with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 19286fd01..0e4a9d842 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1432,6 +1432,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do discoverable = data["discoverable"] || false invisible = data["invisible"] || false actor_type = data["type"] || "Person" + public_key = data["publicKey"]["publicKeyPem"] user_data = %{ ap_id: data["id"], @@ -1449,7 +1450,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do following_address: data["following"], bio: data["summary"], actor_type: actor_type, - also_known_as: Map.get(data, "alsoKnownAs", []) + also_known_as: Map.get(data, "alsoKnownAs", []), + public_key: public_key } # nickname can be nil because of virtual actors -- cgit v1.2.3 From b6bed1a284ce07359642e0a884d2476ca387439d Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 9 Apr 2020 13:01:35 +0200 Subject: Types.URI: New --- lib/pleroma/user.ex | 3 ++- .../activity_pub/object_validators/note_validator.ex | 1 + .../object_validators/types/object_id.ex | 12 +++--------- .../web/activity_pub/object_validators/types/uri.ex | 20 ++++++++++++++++++++ 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 lib/pleroma/web/activity_pub/object_validators/types/uri.ex (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0adea42ec..027386a22 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -28,6 +28,7 @@ defmodule Pleroma.User do alias Pleroma.UserRelationship alias Pleroma.Web alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ObjectValidators.Types alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils @@ -113,7 +114,7 @@ defmodule Pleroma.User do field(:show_role, :boolean, default: true) field(:settings, :map, default: nil) field(:magic_key, :string, default: nil) - field(:uri, :string, default: nil) + field(:uri, Types.Uri, default: nil) field(:hide_followers_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false) field(:hide_followers, :boolean, default: false) diff --git a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex index c95b622e4..462a5620a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex @@ -35,6 +35,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) field(:inRepyTo, :string) + field(:uri, Types.Uri) field(:likes, {:array, :string}, default: []) field(:announcements, {:array, :string}, default: []) diff --git a/lib/pleroma/web/activity_pub/object_validators/types/object_id.ex b/lib/pleroma/web/activity_pub/object_validators/types/object_id.ex index f6e749b33..f71f76370 100644 --- a/lib/pleroma/web/activity_pub/object_validators/types/object_id.ex +++ b/lib/pleroma/web/activity_pub/object_validators/types/object_id.ex @@ -15,15 +15,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do def cast(%{"id" => object}), do: cast(object) - def cast(_) do - :error - end + def cast(_), do: :error - def dump(data) do - {:ok, data} - end + def dump(data), do: {:ok, data} - def load(data) do - {:ok, data} - end + def load(data), do: {:ok, data} end diff --git a/lib/pleroma/web/activity_pub/object_validators/types/uri.ex b/lib/pleroma/web/activity_pub/object_validators/types/uri.ex new file mode 100644 index 000000000..24845bcc0 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/types/uri.ex @@ -0,0 +1,20 @@ +defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.Uri do + use Ecto.Type + + def type, do: :string + + def cast(uri) when is_binary(uri) do + case URI.parse(uri) do + %URI{host: nil} -> :error + %URI{host: ""} -> :error + %URI{scheme: scheme} when scheme in ["https", "http"] -> {:ok, uri} + _ -> :error + end + end + + def cast(_), do: :error + + def dump(data), do: {:ok, data} + + def load(data), do: {:ok, data} +end -- cgit v1.2.3 From 369c03834c5f2638080ff515055723e6c1c716bf Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 1 Apr 2020 07:15:38 +0200 Subject: formatter: Use user.uri instead of user.source_data.uri --- lib/pleroma/formatter.ex | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index c44e7fc8b..02a93a8dc 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -31,7 +31,7 @@ defmodule Pleroma.Formatter do def mention_handler("@" <> nickname, buffer, opts, acc) do case User.get_cached_by_nickname(nickname) do %User{id: id} = user -> - ap_id = get_ap_id(user) + user_url = user.uri || user.ap_id nickname_text = get_nickname_text(nickname, opts) link = @@ -42,7 +42,7 @@ defmodule Pleroma.Formatter do ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)], "data-user": id, class: "u-url mention", - href: ap_id, + href: user_url, rel: "ugc" ), class: "h-card" @@ -146,9 +146,6 @@ defmodule Pleroma.Formatter do end end - defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url - defp get_ap_id(%User{ap_id: ap_id}), do: ap_id - defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname) defp get_nickname_text(nickname, _), do: User.local_nickname(nickname) end -- cgit v1.2.3 From 62656ab259cec1a8585abecf45096b283fa4c60a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 1 Apr 2020 07:47:07 +0200 Subject: User: Move inbox & shared_inbox to own fields --- lib/pleroma/user.ex | 8 ++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 19 +++++++++++++++++-- lib/pleroma/web/activity_pub/publisher.ex | 13 ++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 027386a22..7d8f3a76b 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -134,6 +134,8 @@ defmodule Pleroma.User do field(:skip_thread_containment, :boolean, default: false) field(:actor_type, :string, default: "Person") field(:also_known_as, {:array, :string}, default: []) + field(:inbox, :string) + field(:shared_inbox, :string) embeds_one( :notification_settings, @@ -367,6 +369,8 @@ defmodule Pleroma.User do :bio, :name, :ap_id, + :inbox, + :shared_inbox, :nickname, :public_key, :avatar, @@ -411,6 +415,8 @@ defmodule Pleroma.User do :name, :avatar, :public_key, + :inbox, + :shared_inbox, :locked, :no_rich_text, :default_scope, @@ -508,6 +514,8 @@ defmodule Pleroma.User do :follower_address, :following_address, :public_key, + :inbox, + :shared_inbox, :avatar, :last_refreshed_at, :ap_enabled, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 0e4a9d842..f0bbecc9b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1432,7 +1432,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do discoverable = data["discoverable"] || false invisible = data["invisible"] || false actor_type = data["type"] || "Person" - public_key = data["publicKey"]["publicKeyPem"] + + public_key = + if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do + data["publicKey"]["publicKeyPem"] + else + nil + end + + shared_inbox = + if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do + data["endpoints"]["sharedInbox"] + else + nil + end user_data = %{ ap_id: data["id"], @@ -1451,7 +1464,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do bio: data["summary"], actor_type: actor_type, also_known_as: Map.get(data, "alsoKnownAs", []), - public_key: public_key + public_key: public_key, + inbox: data["inbox"], + shared_inbox: shared_inbox } # nickname can be nil because of virtual actors diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 6c558e7f0..b70cbd043 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -141,8 +141,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do |> Enum.map(& &1.ap_id) end - defp maybe_use_sharedinbox(%User{source_data: data}), - do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] + defp maybe_use_sharedinbox(%User{shared_inbox: nil, inbox: inbox}), do: inbox + defp maybe_use_sharedinbox(%User{shared_inbox: shared_inbox}), do: shared_inbox @doc """ Determine a user inbox to use based on heuristics. These heuristics @@ -157,7 +157,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do """ def determine_inbox( %Activity{data: activity_data}, - %User{source_data: data} = user + %User{inbox: inbox} = user ) do to = activity_data["to"] || [] cc = activity_data["cc"] || [] @@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do maybe_use_sharedinbox(user) true -> - data["inbox"] + inbox end end @@ -192,14 +192,13 @@ defmodule Pleroma.Web.ActivityPub.Publisher do inboxes = recipients |> Enum.filter(&User.ap_enabled?/1) - |> Enum.map(fn %{source_data: data} -> data["inbox"] end) + |> Enum.map(fn actor -> actor.inbox end) |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) |> Instances.filter_reachable() Repo.checkout(fn -> Enum.each(inboxes, fn {inbox, unreachable_since} -> - %User{ap_id: ap_id} = - Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end) + %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end) # Get all the recipients on the same host and add them to cc. Otherwise, a remote # instance would only accept a first message for the first recipient and ignore the rest. -- cgit v1.2.3 From 9172d719ccbf84d55236007d329fc880db69fe42 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 3 Apr 2020 13:03:32 +0200 Subject: profile emojis in User.emoji instead of source_data --- lib/pleroma/emoji/formatter.ex | 14 +++-------- lib/pleroma/user.ex | 27 +++++++++++++++------- lib/pleroma/web/activity_pub/activity_pub.ex | 9 ++++++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 2 +- lib/pleroma/web/activity_pub/views/user_view.ex | 2 +- lib/pleroma/web/common_api/common_api.ex | 20 ---------------- lib/pleroma/web/common_api/utils.ex | 17 +------------- .../mastodon_api/controllers/account_controller.ex | 6 +---- lib/pleroma/web/mastodon_api/views/account_view.ex | 10 ++++---- .../pleroma_api/controllers/account_controller.ex | 15 ++++-------- lib/pleroma/web/static_fe/static_fe_view.ex | 9 -------- .../static_fe/static_fe/_user_card.html.eex | 2 +- .../templates/static_fe/static_fe/profile.html.eex | 2 +- 13 files changed, 45 insertions(+), 90 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/formatter.ex b/lib/pleroma/emoji/formatter.ex index 59ff2cac3..dc45b8a38 100644 --- a/lib/pleroma/emoji/formatter.ex +++ b/lib/pleroma/emoji/formatter.ex @@ -38,22 +38,14 @@ defmodule Pleroma.Emoji.Formatter do def demojify(text, nil), do: text - @doc "Outputs a list of the emoji-shortcodes in a text" - def get_emoji(text) when is_binary(text) do - Enum.filter(Emoji.get_all(), fn {emoji, %Emoji{}} -> - String.contains?(text, ":#{emoji}:") - end) - end - - def get_emoji(_), do: [] - @doc "Outputs a list of the emoji-Maps in a text" def get_emoji_map(text) when is_binary(text) do - get_emoji(text) + Emoji.get_all() + |> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end) |> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc -> Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") end) end - def get_emoji_map(_), do: [] + def get_emoji_map(_), do: %{} end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7d8f3a76b..cd3551e11 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -15,6 +15,7 @@ defmodule Pleroma.User do alias Pleroma.Config alias Pleroma.Conversation.Participation alias Pleroma.Delivery + alias Pleroma.Emoji alias Pleroma.FollowingRelationship alias Pleroma.Formatter alias Pleroma.HTML @@ -124,7 +125,7 @@ defmodule Pleroma.User do field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) - field(:emoji, {:array, :map}, default: []) + field(:emoji, :map, default: %{}) field(:pleroma_settings_store, :map, default: %{}) field(:fields, {:array, :map}, default: []) field(:raw_fields, {:array, :map}, default: []) @@ -368,6 +369,7 @@ defmodule Pleroma.User do [ :bio, :name, + :emoji, :ap_id, :inbox, :shared_inbox, @@ -413,6 +415,7 @@ defmodule Pleroma.User do [ :bio, :name, + :emoji, :avatar, :public_key, :inbox, @@ -443,6 +446,7 @@ defmodule Pleroma.User do |> validate_length(:bio, max: bio_limit) |> validate_length(:name, min: 1, max: name_limit) |> put_fields() + |> put_emoji() |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)}) |> put_change_if_present(:avatar, &put_upload(&1, :avatar)) |> put_change_if_present(:banner, &put_upload(&1, :banner)) @@ -478,6 +482,18 @@ defmodule Pleroma.User do |> elem(0) end + defp put_emoji(changeset) do + bio = get_change(changeset, :bio) + name = get_change(changeset, :name) + + if bio || name do + emoji = Map.merge(Emoji.Formatter.get_emoji_map(bio), Emoji.Formatter.get_emoji_map(name)) + put_change(changeset, :emoji, emoji) + else + changeset + end + end + defp put_change_if_present(changeset, map_field, value_function) do if value = get_change(changeset, map_field) do with {:ok, new_value} <- value_function.(value) do @@ -511,6 +527,7 @@ defmodule Pleroma.User do [ :bio, :name, + :emoji, :follower_address, :following_address, :public_key, @@ -618,7 +635,7 @@ defmodule Pleroma.User do struct |> confirmation_changeset(need_confirmation: need_confirmation?) - |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) + |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation, :emoji]) |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) |> unique_constraint(:email) @@ -1969,12 +1986,6 @@ defmodule Pleroma.User do |> update_and_set_cache() end - def update_source_data(user, source_data) do - user - |> cast(%{source_data: source_data}, [:source_data]) - |> update_and_set_cache() - end - def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do %{ admin: is_admin, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index f0bbecc9b..63502b484 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1427,6 +1427,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) + emojis = + data + |> Map.get("tag", []) + |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) + |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc -> + Map.put(acc, String.trim(name, ":"), url) + end) + locked = data["manuallyApprovesFollowers"] || false data = Transmogrifier.maybe_fix_user_object(data) discoverable = data["discoverable"] || false @@ -1454,6 +1462,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do source_data: data, banner: banner, fields: fields, + emoji: emojis, locked: locked, discoverable: discoverable, invisible: invisible, diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 0a8ad62ad..3d4070fd5 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1129,7 +1129,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def take_emoji_tags(%User{emoji: emoji}) do emoji - |> Enum.flat_map(&Map.to_list/1) + |> Map.to_list() |> Enum.map(&build_emoji_tag/1) end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index bc21ac6c7..d3d79dd5e 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -103,7 +103,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do }, "endpoints" => endpoints, "attachment" => fields, - "tag" => (user.source_data["tag"] || []) ++ emoji_tags, + "tag" => emoji_tags, "discoverable" => user.discoverable } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 636cf3301..952a8d8cb 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -332,26 +332,6 @@ defmodule Pleroma.Web.CommonAPI do defp maybe_create_activity_expiration(result, _), do: result - # Updates the emojis for a user based on their profile - def update(user) do - emoji = emoji_from_profile(user) - source_data = Map.put(user.source_data, "tag", emoji) - - user = - case User.update_source_data(user, source_data) do - {:ok, user} -> user - _ -> user - end - - ActivityPub.update(%{ - local: true, - to: [Pleroma.Constants.as_public(), user.follower_address], - cc: [], - actor: user.ap_id, - object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) - }) - end - def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do with %Activity{ actor: ^user_ap_id, diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 635e7cd38..7eec5aa09 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Conversation.Participation - alias Pleroma.Emoji alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.Plugs.AuthenticationPlug @@ -18,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.User alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.Endpoint alias Pleroma.Web.MediaProxy require Logger @@ -175,7 +173,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do "replies" => %{"type" => "Collection", "totalItems" => 0} } - {note, Map.merge(emoji, Emoji.Formatter.get_emoji_map(option))} + {note, Map.merge(emoji, Pleroma.Emoji.Formatter.get_emoji_map(option))} end) end_time = @@ -431,19 +429,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do end end - def emoji_from_profile(%User{bio: bio, name: name}) do - [bio, name] - |> Enum.map(&Emoji.Formatter.get_emoji/1) - |> Enum.concat() - |> Enum.map(fn {shortcode, %Emoji{file: path}} -> - %{ - "type" => "Emoji", - "icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{path}"}, - "name" => ":#{shortcode}:" - } - end) - end - def maybe_notify_to_recipients( recipients, %Activity{data: %{"to" => to, "type" => _type}} = _activity diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 21bc3d5a5..3fcaa6be6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -140,9 +140,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "PATCH /api/v1/accounts/update_credentials" - def update_credentials(%{assigns: %{user: original_user}} = conn, params) do - user = original_user - + def update_credentials(%{assigns: %{user: user}} = conn, params) do user_params = [ :no_rich_text, @@ -178,8 +176,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do changeset = User.update_changeset(user, user_params) with {:ok, user} <- User.update_and_set_cache(changeset) do - if original_user != user, do: CommonAPI.update(user) - render(conn, "show.json", user: user, for: user, with_pleroma_settings: true) else _e -> render_error(conn, :forbidden, "Invalid request") diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 99e62f580..966032b69 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -180,13 +180,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do bot = user.actor_type in ["Application", "Service"] emojis = - (user.source_data["tag"] || []) - |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) - |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> + Enum.map(user.emoji, fn {shortcode, url} -> %{ - "shortcode" => String.trim(name, ":"), - "url" => MediaProxy.url(url), - "static_url" => MediaProxy.url(url), + "shortcode" => shortcode, + "url" => url, + "static_url" => url, "visible_in_picker" => false } end) diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index dcba67d03..ed4fdfdba 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -13,7 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.StatusView require Pleroma.Constants @@ -58,38 +57,32 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do @doc "PATCH /api/v1/pleroma/accounts/update_avatar" def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do - {:ok, user} = + {:ok, _user} = user |> Changeset.change(%{avatar: nil}) |> User.update_and_set_cache() - CommonAPI.update(user) - json(conn, %{url: nil}) end def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar) - {:ok, user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache() + {:ok, _user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache() %{"url" => [%{"href" => href} | _]} = data - CommonAPI.update(user) - json(conn, %{url: href}) end @doc "PATCH /api/v1/pleroma/accounts/update_banner" def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do - with {:ok, user} <- User.update_banner(user, %{}) do - CommonAPI.update(user) + with {:ok, _user} <- User.update_banner(user, %{}) do json(conn, %{url: nil}) end end def update_banner(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), - {:ok, user} <- User.update_banner(user, object.data) do - CommonAPI.update(user) + {:ok, _user} <- User.update_banner(user, object.data) do %{"url" => [%{"href" => href} | _]} = object.data json(conn, %{url: href}) diff --git a/lib/pleroma/web/static_fe/static_fe_view.ex b/lib/pleroma/web/static_fe/static_fe_view.ex index 66d87620c..b3d1d1ec8 100644 --- a/lib/pleroma/web/static_fe/static_fe_view.ex +++ b/lib/pleroma/web/static_fe/static_fe_view.ex @@ -18,15 +18,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEView do @media_types ["image", "audio", "video"] - def emoji_for_user(%User{} = user) do - user.source_data - |> Map.get("tag", []) - |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) - |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> - {String.trim(name, ":"), url} - end) - end - def fetch_media_type(%{"mediaType" => mediaType}) do Utils.fetch_media_type(@media_types, mediaType) end diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex index 2a7582d45..56f3a1524 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex @@ -4,7 +4,7 @@ - <%= raw (@user.name |> Formatter.emojify(emoji_for_user(@user))) %> + <%= raw Formatter.emojify(@user.name, @user.emoji) %> <%= @user.nickname %> diff --git a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex index e7d2aecad..3191bf450 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex @@ -7,7 +7,7 @@ - <%= raw Formatter.emojify(@user.name, emoji_for_user(@user)) %> | + <%= raw Formatter.emojify(@user.name, @user.emoji) %> | <%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>

<%= raw @user.bio %>

-- cgit v1.2.3 From 3420dec494203b46d37ddc17f7e1235dc908a5b3 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 6 Apr 2020 10:44:48 +0200 Subject: Remove User.fields/1 --- lib/pleroma/user.ex | 19 +------------------ lib/pleroma/web/activity_pub/views/user_view.ex | 5 +---- 2 files changed, 2 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index cd3551e11..79e9b2c86 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1993,21 +1993,6 @@ defmodule Pleroma.User do } end - # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. - # For example: [{"name": "Pronoun", "value": "she/her"}, …] - def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do - limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0) - - attachment - |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) - |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) - |> Enum.take(limit) - end - - def fields(%{fields: nil}), do: [] - - def fields(%{fields: fields}), do: fields - def validate_fields(changeset, remote? \\ false) do limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields limit = Pleroma.Config.get([:instance, limit_name], 0) @@ -2195,9 +2180,7 @@ defmodule Pleroma.User do # - display name def sanitize_html(%User{} = user, filter) do fields = - user - |> User.fields() - |> Enum.map(fn %{"name" => name, "value" => value} -> + Enum.map(user.fields, fn %{"name" => name, "value" => value} -> %{ "name" => name, "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index d3d79dd5e..34590b16d 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -79,10 +79,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do emoji_tags = Transmogrifier.take_emoji_tags(user) - fields = - user - |> User.fields() - |> Enum.map(&Map.put(&1, "type", "PropertyValue")) + fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue")) %{ "id" => user.ap_id, -- cgit v1.2.3 From e89078ac2a27bb0a833c982dbb5eef63ddea3cc0 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 6 Apr 2020 10:59:35 +0200 Subject: User: remove source_data --- lib/pleroma/user.ex | 3 --- lib/pleroma/web/activity_pub/activity_pub.ex | 1 - 2 files changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 79e9b2c86..d05dfb480 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -97,7 +97,6 @@ defmodule Pleroma.User do field(:last_digest_emailed_at, :naive_datetime) field(:banner, :map, default: %{}) field(:background, :map, default: %{}) - field(:source_data, :map, default: %{}) field(:note_count, :integer, default: 0) field(:follower_count, :integer, default: 0) field(:following_count, :integer, default: 0) @@ -377,7 +376,6 @@ defmodule Pleroma.User do :public_key, :avatar, :ap_enabled, - :source_data, :banner, :locked, :magic_key, @@ -536,7 +534,6 @@ defmodule Pleroma.User do :avatar, :last_refreshed_at, :ap_enabled, - :source_data, :banner, :locked, :magic_key, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 63502b484..9b832f4cb 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1459,7 +1459,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ap_id: data["id"], uri: get_actor_url(data["url"]), ap_enabled: true, - source_data: data, banner: banner, fields: fields, emoji: emojis, -- cgit v1.2.3 From 88b16fdfb7b40877aecae5d45f6f3a1c54362f13 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sat, 11 Apr 2020 16:01:09 +0300 Subject: [#1364] Disabled notifications on activities from blocked domains. --- lib/pleroma/notification.ex | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 04ee510b9..02363ddb0 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -321,10 +321,11 @@ defmodule Pleroma.Notification do @doc """ Returns a tuple with 2 elements: - {enabled notification receivers, currently disabled receivers (blocking / [thread] muting)} + {notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)} NOTE: might be called for FAKE Activities, see ActivityPub.Utils.get_notified_from_object/1 """ + @spec get_notified_from_activity(Activity.t(), boolean()) :: {list(User.t()), list(User.t())} def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) @@ -337,17 +338,22 @@ defmodule Pleroma.Notification do |> Utils.maybe_notify_followers(activity) |> Enum.uniq() - # Since even subscribers and followers can mute / thread-mute, filtering all above AP IDs - notification_enabled_ap_ids = - potential_receiver_ap_ids - |> exclude_relationship_restricted_ap_ids(activity) - |> exclude_thread_muter_ap_ids(activity) - potential_receivers = potential_receiver_ap_ids |> Enum.uniq() |> User.get_users_from_set(local_only) + activity_actor_domain = activity.actor && URI.parse(activity.actor).host + + notification_enabled_ap_ids = + for u <- potential_receivers, activity_actor_domain not in u.domain_blocks, do: u.ap_id + + # Since even subscribers and followers can mute / thread-mute, filtering all above AP IDs + notification_enabled_ap_ids = + notification_enabled_ap_ids + |> exclude_relationship_restricted_ap_ids(activity) + |> exclude_thread_muter_ap_ids(activity) + notification_enabled_users = Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end) -- cgit v1.2.3 From c077ad0b3305e74f5b8d1b9bf38d4f480d76c1a6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 11 Apr 2020 21:44:52 +0300 Subject: Remove User.upgrade_changeset in favor of remote_user_creation The two changesets had the same purpose, yet some changes were updated in one, but not the other (`uri`, for example). Also makes `Transmogrifier.upgrade_user_from_ap_id` be called from `ActivityPub.make_user_from_ap_id` only when the user is actually not AP enabled yet. I did not bother rewriting tests that used `User.insert_or_update` to use the changeset instead because they seemed to just test the implementation, rather than behavior. --- lib/pleroma/user.ex | 60 +++----------------------- lib/pleroma/web/activity_pub/activity_pub.ex | 15 ++++++- lib/pleroma/web/activity_pub/transmogrifier.ex | 14 +++--- 3 files changed, 24 insertions(+), 65 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 71c8c3a4e..fab405233 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -339,18 +339,20 @@ defmodule Pleroma.User do end end - def remote_user_creation(params) do + def remote_user_changeset(struct \\ %User{local: false}, params) do bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) params = params + |> Map.put(:name, blank?(params[:name]) || params[:nickname]) + |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now()) |> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:bio, bio_limit) |> truncate_fields_param() changeset = - %User{local: false} + struct |> cast( params, [ @@ -375,7 +377,8 @@ defmodule Pleroma.User do :discoverable, :invisible, :actor_type, - :also_known_as + :also_known_as, + :last_refreshed_at ] ) |> validate_required([:name, :ap_id]) @@ -488,49 +491,6 @@ defmodule Pleroma.User do end end - def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do - bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) - name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) - - params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now()) - - params = if remote?, do: truncate_fields_param(params), else: params - - struct - |> cast( - params, - [ - :bio, - :name, - :follower_address, - :following_address, - :avatar, - :last_refreshed_at, - :ap_enabled, - :source_data, - :banner, - :locked, - :magic_key, - :follower_count, - :following_count, - :hide_follows, - :fields, - :hide_followers, - :allow_following_move, - :discoverable, - :hide_followers_count, - :hide_follows_count, - :actor_type, - :also_known_as - ] - ) - |> unique_constraint(:nickname) - |> validate_format(:nickname, local_nickname_regex()) - |> validate_length(:bio, max: bio_limit) - |> validate_length(:name, max: name_limit) - |> validate_fields(remote?) - end - def update_as_admin_changeset(struct, params) do struct |> update_changeset(params) @@ -1642,14 +1602,6 @@ defmodule Pleroma.User do defp blank?(""), do: nil defp blank?(n), do: n - def insert_or_update_user(data) do - data - |> Map.put(:name, blank?(data[:name]) || data[:nickname]) - |> remote_user_creation() - |> Repo.insert(on_conflict: {:replace_all_except, [:id]}, conflict_target: :nickname) - |> set_cache() - end - def ap_enabled?(%User{local: true}), do: true def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled def ap_enabled?(_), do: false diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 86b105b7f..2602b966b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1551,11 +1551,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def make_user_from_ap_id(ap_id) do - if _user = User.get_cached_by_ap_id(ap_id) do + user = User.get_cached_by_ap_id(ap_id) + + if user && !User.ap_enabled?(user) do Transmogrifier.upgrade_user_from_ap_id(ap_id) else with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do - User.insert_or_update_user(data) + if user do + user + |> User.remote_user_changeset(data) + |> User.update_and_set_cache() + else + data + |> User.remote_user_changeset() + |> Repo.insert() + |> User.set_cache() + end else e -> {:error, e} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index f9951cc5d..18fd56bed 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -710,7 +710,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) actor - |> User.upgrade_changeset(new_user_data, true) + |> User.remote_user_changeset(new_user_data) |> User.update_and_set_cache() ActivityPub.update(%{ @@ -1253,12 +1253,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def upgrade_user_from_ap_id(ap_id) do with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), - already_ap <- User.ap_enabled?(user), - {:ok, user} <- upgrade_user(user, data) do - if not already_ap do - TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) - end - + {:ok, user} <- update_user(user, data) do + TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) {:ok, user} else %User{} = user -> {:ok, user} @@ -1266,9 +1262,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - defp upgrade_user(user, data) do + defp update_user(user, data) do user - |> User.upgrade_changeset(data, true) + |> User.remote_user_changeset(data) |> User.update_and_set_cache() end -- cgit v1.2.3 From c556efb761a3e7fc2beb4540d6f58dbfe8e4abfe Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 12 Apr 2020 21:53:03 +0300 Subject: [#1364] Enabled notifications on followed domain-blocked users' activities. --- lib/pleroma/following_relationship.ex | 35 ++++++++++++++++++--- lib/pleroma/notification.ex | 59 ++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index a9538ea4e..11e06c5cc 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -69,6 +69,29 @@ defmodule Pleroma.FollowingRelationship do |> Repo.aggregate(:count, :id) end + def followers_query(%User{} = user) do + __MODULE__ + |> join(:inner, [r], u in User, on: r.follower_id == u.id) + |> where([r], r.following_id == ^user.id) + |> where([r], r.state == "accept") + end + + def followers_ap_ids(%User{} = user, from_ap_ids \\ nil) do + query = + user + |> followers_query() + |> select([r, u], u.ap_id) + + query = + if from_ap_ids do + where(query, [r, u], u.ap_id in ^from_ap_ids) + else + query + end + + Repo.all(query) + end + def following_count(%User{id: nil}), do: 0 def following_count(%User{} = user) do @@ -92,12 +115,16 @@ defmodule Pleroma.FollowingRelationship do |> Repo.exists?() end + def following_query(%User{} = user) do + __MODULE__ + |> join(:inner, [r], u in User, on: r.following_id == u.id) + |> where([r], r.follower_id == ^user.id) + |> where([r], r.state == "accept") + end + def following(%User{} = user) do following = - __MODULE__ - |> join(:inner, [r], u in User, on: r.following_id == u.id) - |> where([r], r.follower_id == ^user.id) - |> where([r], r.state == "accept") + following_query(user) |> select([r, u], u.follower_address) |> Repo.all() diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 02363ddb0..da05ff2e4 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Notification do use Ecto.Schema alias Pleroma.Activity + alias Pleroma.FollowingRelationship alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Pagination @@ -81,6 +82,7 @@ defmodule Pleroma.Notification do |> exclude_visibility(opts) end + # Excludes blocked users and non-followed domain-blocked users defp exclude_blocked(query, user, opts) do blocked_ap_ids = opts[:blocked_users_ap_ids] || User.blocked_users_ap_ids(user) @@ -88,7 +90,16 @@ defmodule Pleroma.Notification do |> where([n, a], a.actor not in ^blocked_ap_ids) |> where( [n, a], - fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks + fragment( + # "NOT (actor's domain in domain_blocks) OR (actor is in followed AP IDs)" + "NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR \ + ? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr \ + ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = 'accept')", + a.actor, + ^user.domain_blocks, + a.actor, + ^User.binary_id(user.id) + ) ) end @@ -338,19 +349,11 @@ defmodule Pleroma.Notification do |> Utils.maybe_notify_followers(activity) |> Enum.uniq() - potential_receivers = - potential_receiver_ap_ids - |> Enum.uniq() - |> User.get_users_from_set(local_only) - - activity_actor_domain = activity.actor && URI.parse(activity.actor).host - - notification_enabled_ap_ids = - for u <- potential_receivers, activity_actor_domain not in u.domain_blocks, do: u.ap_id + potential_receivers = User.get_users_from_set(potential_receiver_ap_ids, local_only) - # Since even subscribers and followers can mute / thread-mute, filtering all above AP IDs notification_enabled_ap_ids = - notification_enabled_ap_ids + potential_receiver_ap_ids + |> exclude_domain_blocker_ap_ids(activity, potential_receivers) |> exclude_relationship_restricted_ap_ids(activity) |> exclude_thread_muter_ap_ids(activity) @@ -362,6 +365,38 @@ defmodule Pleroma.Notification do def get_notified_from_activity(_, _local_only), do: {[], []} + @doc "Filters out AP IDs of users who domain-block and not follow activity actor" + def exclude_domain_blocker_ap_ids(ap_ids, activity, preloaded_users \\ []) + + def exclude_domain_blocker_ap_ids([], _activity, _preloaded_users), do: [] + + def exclude_domain_blocker_ap_ids(ap_ids, %Activity{} = activity, preloaded_users) do + activity_actor_domain = activity.actor && URI.parse(activity.actor).host + + users = + ap_ids + |> Enum.map(fn ap_id -> + Enum.find(preloaded_users, &(&1.ap_id == ap_id)) || + User.get_cached_by_ap_id(ap_id) + end) + |> Enum.filter(& &1) + + domain_blocker_ap_ids = for u <- users, activity_actor_domain in u.domain_blocks, do: u.ap_id + + domain_blocker_follower_ap_ids = + if Enum.any?(domain_blocker_ap_ids) do + activity + |> Activity.user_actor() + |> FollowingRelationship.followers_ap_ids(domain_blocker_ap_ids) + else + [] + end + + ap_ids + |> Kernel.--(domain_blocker_ap_ids) + |> Kernel.++(domain_blocker_follower_ap_ids) + end + @doc "Filters out AP IDs of users basing on their relationships with activity actor user" def exclude_relationship_restricted_ap_ids([], _activity), do: [] -- cgit v1.2.3 From 9a3c74b244bce6097a8c6da99692bfc9973e1ec8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 12 Apr 2020 20:26:35 -0500 Subject: Always accept deletions through SimplePolicy --- lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 4edc007fd..b23f263f5 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -148,6 +148,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_banner_removal(_actor_info, object), do: {:ok, object} + @impl true + def filter(%{"type" => "Delete"} = object), do: {:ok, object} + @impl true def filter(%{"actor" => actor} = object) do actor_info = URI.parse(actor) -- cgit v1.2.3 From 99b0bc198921099816a5f809f11a7579b3993274 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 13 Apr 2020 13:24:31 +0300 Subject: [#1364] Resolved merge conflicts with `develop`. Refactoring. --- lib/pleroma/following_relationship.ex | 34 ++++++++++++++++++++++++++++++++-- lib/pleroma/notification.ex | 14 +------------- 2 files changed, 33 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 219a64352..3a3082e72 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -10,11 +10,12 @@ defmodule Pleroma.FollowingRelationship do alias Ecto.Changeset alias FlakeId.Ecto.CompatType + alias Pleroma.FollowingRelationship.State alias Pleroma.Repo alias Pleroma.User schema "following_relationships" do - field(:state, Pleroma.FollowingRelationship.State, default: :follow_pending) + field(:state, State, default: :follow_pending) belongs_to(:follower, User, type: CompatType) belongs_to(:following, User, type: CompatType) @@ -22,6 +23,11 @@ defmodule Pleroma.FollowingRelationship do timestamps() end + @doc "Returns underlying integer code for state atom" + def state_int_code(state_atom), do: State.__enum_map__() |> Keyword.fetch!(state_atom) + + def accept_state_code, do: state_int_code(:follow_accept) + def changeset(%__MODULE__{} = following_relationship, attrs) do following_relationship |> cast(attrs, [:state]) @@ -86,7 +92,7 @@ defmodule Pleroma.FollowingRelationship do __MODULE__ |> join(:inner, [r], u in User, on: r.follower_id == u.id) |> where([r], r.following_id == ^user.id) - |> where([r], r.state == "accept") + |> where([r], r.state == ^:follow_accept) end def followers_ap_ids(%User{} = user, from_ap_ids \\ nil) do @@ -198,6 +204,30 @@ defmodule Pleroma.FollowingRelationship do end) end + @doc """ + For a query with joined activity, + keeps rows where activity's actor is followed by user -or- is NOT domain-blocked by user. + """ + def keep_following_or_not_domain_blocked(query, user) do + where( + query, + [_, activity], + fragment( + # "(actor's domain NOT in domain_blocks) OR (actor IS in followed AP IDs)" + """ + NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR + ? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr + ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?) + """, + activity.actor, + ^user.domain_blocks, + activity.actor, + ^User.binary_id(user.id), + ^accept_state_code() + ) + ) + end + defp validate_not_self_relationship(%Changeset{} = changeset) do changeset |> validate_follower_id_following_id_inequality() diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index da05ff2e4..b76dd176c 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -88,19 +88,7 @@ defmodule Pleroma.Notification do query |> where([n, a], a.actor not in ^blocked_ap_ids) - |> where( - [n, a], - fragment( - # "NOT (actor's domain in domain_blocks) OR (actor is in followed AP IDs)" - "NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR \ - ? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr \ - ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = 'accept')", - a.actor, - ^user.domain_blocks, - a.actor, - ^User.binary_id(user.id) - ) - ) + |> FollowingRelationship.keep_following_or_not_domain_blocked(user) end defp exclude_notification_muted(query, _, %{@include_muted_option => true}) do -- cgit v1.2.3 From b08ded6c2f5ee29c6efc8c67cfc2ce0a679f0c77 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 3 Apr 2020 22:45:08 +0400 Subject: Add spec for AccountController.create --- .../web/api_spec/operations/account_operation.ex | 68 +++++++++++++ lib/pleroma/web/api_spec/render_error.ex | 27 ++++++ .../web/api_spec/schemas/account_create_request.ex | 56 +++++++++++ .../api_spec/schemas/account_create_response.ex | 29 ++++++ .../mastodon_api/controllers/account_controller.ex | 36 ++++--- lib/pleroma/web/twitter_api/twitter_api.ex | 106 +++++++++------------ 6 files changed, 246 insertions(+), 76 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/account_operation.ex create mode 100644 lib/pleroma/web/api_spec/render_error.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_create_request.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_create_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex new file mode 100644 index 000000000..9085f1af1 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.AccountOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest + alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Helpers + + @spec open_api_operation(atom) :: Operation.t() + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + @spec create_operation() :: Operation.t() + def create_operation do + %Operation{ + tags: ["accounts"], + summary: "Register an account", + description: + "Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.", + operationId: "AccountController.create", + requestBody: Helpers.request_body("Parameters", AccountCreateRequest, required: true), + responses: %{ + 200 => Operation.response("Account", "application/json", AccountCreateResponse) + } + } + end + + def verify_credentials_operation do + :ok + end + + def update_credentials_operation do + :ok + end + + def relationships_operation do + :ok + end + + def show_operation do + :ok + end + + def statuses_operation do + :ok + end + + def followers_operation do + :ok + end + + def following_operation, do: :ok + def lists_operation, do: :ok + def follow_operation, do: :ok + def unfollow_operation, do: :ok + def mute_operation, do: :ok + def unmute_operation, do: :ok + def block_operation, do: :ok + def unblock_operation, do: :ok + def follows_operation, do: :ok + def mutes_operation, do: :ok + def blocks_operation, do: :ok + def endorsements_operation, do: :ok +end diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex new file mode 100644 index 000000000..e063d115b --- /dev/null +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -0,0 +1,27 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.RenderError do + @behaviour Plug + + alias Plug.Conn + alias OpenApiSpex.Plug.JsonRenderError + + @impl Plug + def init(opts), do: opts + + @impl Plug + + def call(%{private: %{open_api_spex: %{operation_id: "AccountController.create"}}} = conn, _) do + conn + |> Conn.put_status(:bad_request) + |> Phoenix.Controller.json(%{"error" => "Missing parameters"}) + end + + def call(conn, reason) do + opts = JsonRenderError.init(reason) + + JsonRenderError.call(conn, opts) + end +end diff --git a/lib/pleroma/web/api_spec/schemas/account_create_request.ex b/lib/pleroma/web/api_spec/schemas/account_create_request.ex new file mode 100644 index 000000000..398e2d613 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_create_request.ex @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest do + alias OpenApiSpex.Schema + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountCreateRequest", + description: "POST body for creating an account", + type: :object, + properties: %{ + reason: %Schema{ + type: :string, + description: + "Text that will be reviewed by moderators if registrations require manual approval" + }, + username: %Schema{type: :string, description: "The desired username for the account"}, + email: %Schema{ + type: :string, + description: + "The email address to be used for login. Required when `account_activation_required` is enabled.", + format: :email + }, + password: %Schema{type: :string, description: "The password to be used for login"}, + agreement: %Schema{ + type: :boolean, + description: + "Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE." + }, + locale: %Schema{ + type: :string, + description: "The language of the confirmation email that will be sent" + }, + # Pleroma-specific properties: + fullname: %Schema{type: :string, description: "Full name"}, + bio: %Schema{type: :string, description: "Bio", default: ""}, + captcha_solution: %Schema{type: :string, description: "Provider-specific captcha solution"}, + captcha_token: %Schema{type: :string, description: "Provider-specific captcha token"}, + captcha_answer_data: %Schema{type: :string, description: "Provider-specific captcha data"}, + token: %Schema{ + type: :string, + description: "Invite token required when the registrations aren't public" + } + }, + required: [:username, :password, :agreement], + example: %{ + "username" => "cofe", + "email" => "cofe@example.com", + "password" => "secret", + "agreement" => "true", + "bio" => "☕️" + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_create_response.ex b/lib/pleroma/web/api_spec/schemas/account_create_response.ex new file mode 100644 index 000000000..f41a034c0 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_create_response.ex @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountCreateResponse", + description: "Response schema for an account", + type: :object, + properties: %{ + token_type: %Schema{type: :string}, + access_token: %Schema{type: :string}, + scope: %Schema{type: :array, items: %Schema{type: :string}}, + created_at: %Schema{type: :integer} + }, + example: %{ + "JSON" => %{ + "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk", + "created_at" => 1_585_918_714, + "scope" => ["read", "write", "follow", "push"], + "token_type" => "Bearer" + } + } + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 7da1a11f6..eb082daf8 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -80,27 +80,33 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(RateLimiter, [name: :app_account_creation] when action == :create) plug(:assign_account_by_id when action in @needs_account) + plug( + OpenApiSpex.Plug.CastAndValidate, + [render_error: Pleroma.Web.ApiSpec.RenderError] when action == :create + ) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation + @doc "POST /api/v1/accounts" - def create( - %{assigns: %{app: app}} = conn, - %{"username" => nickname, "password" => _, "agreement" => true} = params - ) do + def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do params = params |> Map.take([ - "email", - "captcha_solution", - "captcha_token", - "captcha_answer_data", - "token", - "password" + :email, + :bio, + :captcha_solution, + :captcha_token, + :captcha_answer_data, + :token, + :password, + :fullname ]) - |> Map.put("nickname", nickname) - |> Map.put("fullname", params["fullname"] || nickname) - |> Map.put("bio", params["bio"] || "") - |> Map.put("confirm", params["password"]) + |> Map.put(:nickname, params.username) + |> Map.put(:fullname, params.fullname || params.username) + |> Map.put(:bio, params.bio || "") + |> Map.put(:confirm, params.password) with :ok <- validate_email_param(params), {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), @@ -124,7 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do render_error(conn, :forbidden, "Invalid credentials") end - defp validate_email_param(%{"email" => _}), do: :ok + defp validate_email_param(%{:email => email}) when not is_nil(email), do: :ok defp validate_email_param(_) do case Pleroma.Config.get([:instance, :account_activation_required]) do diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index f9c0994da..37be48b5a 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -12,72 +12,56 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do require Pleroma.Constants def register_user(params, opts \\ []) do - token = params["token"] - - params = %{ - nickname: params["nickname"], - name: params["fullname"], - bio: User.parse_bio(params["bio"]), - email: params["email"], - password: params["password"], - password_confirmation: params["confirm"], - captcha_solution: params["captcha_solution"], - captcha_token: params["captcha_token"], - captcha_answer_data: params["captcha_answer_data"] - } - - captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled]) - # true if captcha is disabled or enabled and valid, false otherwise - captcha_ok = - if not captcha_enabled do - :ok - else - Pleroma.Captcha.validate( - params[:captcha_token], - params[:captcha_solution], - params[:captcha_answer_data] - ) - end - - # Captcha invalid - if captcha_ok != :ok do - {:error, error} = captcha_ok - # I have no idea how this error handling works - {:error, %{error: Jason.encode!(%{captcha: [error]})}} - else - registration_process( - params, - %{ - registrations_open: Pleroma.Config.get([:instance, :registrations_open]), - token: token - }, - opts - ) + params = + params + |> Map.take([ + :nickname, + :password, + :captcha_solution, + :captcha_token, + :captcha_answer_data, + :token, + :email + ]) + |> Map.put(:bio, User.parse_bio(params[:bio] || "")) + |> Map.put(:name, params.fullname) + |> Map.put(:password_confirmation, params[:confirm]) + + case validate_captcha(params) do + :ok -> + if Pleroma.Config.get([:instance, :registrations_open]) do + create_user(params, opts) + else + create_user_with_invite(params, opts) + end + + {:error, error} -> + # I have no idea how this error handling works + {:error, %{error: Jason.encode!(%{captcha: [error]})}} end end - defp registration_process(params, %{registrations_open: true}, opts) do - create_user(params, opts) + defp validate_captcha(params) do + if Pleroma.Config.get([Pleroma.Captcha, :enabled]) do + Pleroma.Captcha.validate( + params.captcha_token, + params.captcha_solution, + params.captcha_answer_data + ) + else + :ok + end end - defp registration_process(params, %{token: token}, opts) do - invite = - unless is_nil(token) do - Repo.get_by(UserInviteToken, %{token: token}) - end - - valid_invite? = invite && UserInviteToken.valid_invite?(invite) - - case invite do - nil -> - {:error, "Invalid token"} - - invite when valid_invite? -> - UserInviteToken.update_usage!(invite) - create_user(params, opts) - - _ -> - {:error, "Expired token"} + defp create_user_with_invite(params, opts) do + with %{token: token} when is_binary(token) <- params, + %UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}), + true <- UserInviteToken.valid_invite?(invite) do + UserInviteToken.update_usage!(invite) + create_user(params, opts) + else + nil -> {:error, "Invalid token"} + _ -> {:error, "Expired token"} end end -- cgit v1.2.3 From f80116125f928de36c93627bbdf5f6578396f53b Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 6 Apr 2020 00:15:37 +0400 Subject: Add spec for AccountController.verify_credentials --- lib/pleroma/web/api_spec.ex | 2 +- .../web/api_spec/operations/account_operation.ex | 14 +- .../web/api_spec/operations/app_operation.ex | 6 +- lib/pleroma/web/api_spec/render_error.ex | 2 +- lib/pleroma/web/api_spec/schemas/account.ex | 181 +++++++++++++++++++++ lib/pleroma/web/api_spec/schemas/account_emoji.ex | 31 ++++ lib/pleroma/web/api_spec/schemas/account_field.ex | 28 ++++ 7 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_emoji.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_field.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index 41e48a085..c85fe30d1 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -31,7 +31,7 @@ defmodule Pleroma.Web.ApiSpec do password: %OpenApiSpex.OAuthFlow{ authorizationUrl: "/oauth/authorize", tokenUrl: "/oauth/token", - scopes: %{"read" => "read"} + scopes: %{"read" => "read", "write" => "write"} } } } diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 9085f1af1..3d2270c29 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -4,9 +4,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.Helpers + alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse - alias Pleroma.Web.ApiSpec.Helpers @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -30,7 +31,16 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def verify_credentials_operation do - :ok + %Operation{ + tags: ["accounts"], + description: "Test to make sure that the user token works.", + summary: "Verify account credentials", + operationId: "AccountController.verify_credentials", + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => Operation.response("Account", "application/json", Account) + } + } end def update_credentials_operation do diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 26d8dbd42..935215c64 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -51,11 +51,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do summary: "Verify your app works", description: "Confirm that the app's OAuth2 credentials work.", operationId: "AppController.verify_credentials", - security: [ - %{ - "oAuth" => ["read"] - } - ], + security: [%{"oAuth" => ["read"]}], responses: %{ 200 => Operation.response("App", "application/json", %Schema{ diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index e063d115b..9184c43b6 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -5,8 +5,8 @@ defmodule Pleroma.Web.ApiSpec.RenderError do @behaviour Plug - alias Plug.Conn alias OpenApiSpex.Plug.JsonRenderError + alias Plug.Conn @impl Plug def init(opts), do: opts diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex new file mode 100644 index 000000000..59c4ac4a4 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -0,0 +1,181 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Account do + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.AccountField + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Account", + description: "Response schema for an account", + type: :object, + properties: %{ + acct: %Schema{type: :string}, + avatar_static: %Schema{type: :string}, + avatar: %Schema{type: :string}, + bot: %Schema{type: :boolean}, + created_at: %Schema{type: :string, format: "date-time"}, + display_name: %Schema{type: :string}, + emojis: %Schema{type: :array, items: AccountEmoji}, + fields: %Schema{type: :array, items: AccountField}, + follow_requests_count: %Schema{type: :integer}, + followers_count: %Schema{type: :integer}, + following_count: %Schema{type: :integer}, + header_static: %Schema{type: :string}, + header: %Schema{type: :string}, + id: %Schema{type: :string}, + locked: %Schema{type: :boolean}, + note: %Schema{type: :string}, + statuses_count: %Schema{type: :integer}, + url: %Schema{type: :string}, + username: %Schema{type: :string}, + pleroma: %Schema{ + type: :object, + properties: %{ + allow_following_move: %Schema{type: :boolean}, + background_image: %Schema{type: :boolean, nullable: true}, + chat_token: %Schema{type: :string}, + confirmation_pending: %Schema{type: :boolean}, + hide_favorites: %Schema{type: :boolean}, + hide_followers_count: %Schema{type: :boolean}, + hide_followers: %Schema{type: :boolean}, + hide_follows_count: %Schema{type: :boolean}, + hide_follows: %Schema{type: :boolean}, + is_admin: %Schema{type: :boolean}, + is_moderator: %Schema{type: :boolean}, + skip_thread_containment: %Schema{type: :boolean}, + tags: %Schema{type: :array, items: %Schema{type: :string}}, + unread_conversation_count: %Schema{type: :integer}, + notification_settings: %Schema{ + type: :object, + properties: %{ + followers: %Schema{type: :boolean}, + follows: %Schema{type: :boolean}, + non_followers: %Schema{type: :boolean}, + non_follows: %Schema{type: :boolean}, + privacy_option: %Schema{type: :boolean} + } + }, + relationship: %Schema{ + type: :object, + properties: %{ + blocked_by: %Schema{type: :boolean}, + blocking: %Schema{type: :boolean}, + domain_blocking: %Schema{type: :boolean}, + endorsed: %Schema{type: :boolean}, + followed_by: %Schema{type: :boolean}, + following: %Schema{type: :boolean}, + id: %Schema{type: :string}, + muting: %Schema{type: :boolean}, + muting_notifications: %Schema{type: :boolean}, + requested: %Schema{type: :boolean}, + showing_reblogs: %Schema{type: :boolean}, + subscribing: %Schema{type: :boolean} + } + }, + settings_store: %Schema{ + type: :object + } + } + }, + source: %Schema{ + type: :object, + properties: %{ + fields: %Schema{type: :array, items: AccountField}, + note: %Schema{type: :string}, + privacy: %Schema{type: :string}, + sensitive: %Schema{type: :boolean}, + pleroma: %Schema{ + type: :object, + properties: %{ + actor_type: %Schema{type: :string}, + discoverable: %Schema{type: :boolean}, + no_rich_text: %Schema{type: :boolean}, + show_role: %Schema{type: :boolean} + } + } + } + } + }, + example: %{ + "JSON" => %{ + "acct" => "foobar", + "avatar" => "https://mypleroma.com/images/avi.png", + "avatar_static" => "https://mypleroma.com/images/avi.png", + "bot" => false, + "created_at" => "2020-03-24T13:05:58.000Z", + "display_name" => "foobar", + "emojis" => [], + "fields" => [], + "follow_requests_count" => 0, + "followers_count" => 0, + "following_count" => 1, + "header" => "https://mypleroma.com/images/banner.png", + "header_static" => "https://mypleroma.com/images/banner.png", + "id" => "9tKi3esbG7OQgZ2920", + "locked" => false, + "note" => "cofe", + "pleroma" => %{ + "allow_following_move" => true, + "background_image" => nil, + "confirmation_pending" => true, + "hide_favorites" => true, + "hide_followers" => false, + "hide_followers_count" => false, + "hide_follows" => false, + "hide_follows_count" => false, + "is_admin" => false, + "is_moderator" => false, + "skip_thread_containment" => false, + "chat_token" => + "SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc", + "unread_conversation_count" => 0, + "tags" => [], + "notification_settings" => %{ + "followers" => true, + "follows" => true, + "non_followers" => true, + "non_follows" => true, + "privacy_option" => false + }, + "relationship" => %{ + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => false, + "id" => "9tKi3esbG7OQgZ2920", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false + }, + "settings_store" => %{ + "pleroma-fe" => %{} + } + }, + "source" => %{ + "fields" => [], + "note" => "foobar", + "pleroma" => %{ + "actor_type" => "Person", + "discoverable" => false, + "no_rich_text" => false, + "show_role" => true + }, + "privacy" => "public", + "sensitive" => false + }, + "statuses_count" => 0, + "url" => "https://mypleroma.com/users/foobar", + "username" => "foobar" + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_emoji.ex b/lib/pleroma/web/api_spec/schemas/account_emoji.ex new file mode 100644 index 000000000..403b13b15 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_emoji.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountEmoji do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountEmoji", + description: "Response schema for account custom fields", + type: :object, + properties: %{ + shortcode: %Schema{type: :string}, + url: %Schema{type: :string}, + static_url: %Schema{type: :string}, + visible_in_picker: %Schema{type: :boolean} + }, + example: %{ + "JSON" => %{ + "shortcode" => "fatyoshi", + "url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png", + "static_url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png", + "visible_in_picker" => true + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_field.ex b/lib/pleroma/web/api_spec/schemas/account_field.ex new file mode 100644 index 000000000..8906d812d --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_field.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountField", + description: "Response schema for account custom fields", + type: :object, + properties: %{ + name: %Schema{type: :string}, + value: %Schema{type: :string}, + verified_at: %Schema{type: :string, format: "date-time", nullable: true} + }, + example: %{ + "JSON" => %{ + "name" => "Website", + "value" => + "https://pleroma.com", + "verified_at" => "2019-08-29T04:14:55.571+00:00" + } + } + }) +end -- cgit v1.2.3 From 260cbddc943e53a85762e56852de65d2b900cc04 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 7 Apr 2020 14:53:12 +0400 Subject: Add spec for AccountController.update_credentials --- lib/pleroma/web/api_spec/helpers.ex | 2 +- .../web/api_spec/operations/account_operation.ex | 14 ++- .../api_spec/schemas/account_field_attribute.ex | 26 +++++ .../schemas/account_update_credentials_request.ex | 123 +++++++++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 41 ++++--- 5 files changed, 190 insertions(+), 16 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account_field_attribute.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index 35cf4c0d8..7348dcbee 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Web.ApiSpec.Helpers do def request_body(description, schema_ref, opts \\ []) do - media_types = ["application/json", "multipart/form-data"] + media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"] content = media_types diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 3d2270c29..d7b56cc2b 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -44,7 +45,18 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def update_credentials_operation do - :ok + %Operation{ + tags: ["accounts"], + summary: "Update account credentials", + description: "Update the user's display and preferences.", + operationId: "AccountController.update_credentials", + security: [%{"oAuth" => ["write:accounts"]}], + requestBody: + Helpers.request_body("Parameters", AccountUpdateCredentialsRequest, required: true), + responses: %{ + 200 => Operation.response("Account", "application/json", Account) + } + } end def relationships_operation do diff --git a/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex b/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex new file mode 100644 index 000000000..fbbdf95f5 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex @@ -0,0 +1,26 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountAttributeField do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountAttributeField", + description: "Request schema for account custom fields", + type: :object, + properties: %{ + name: %Schema{type: :string}, + value: %Schema{type: :string} + }, + required: [:name, :value], + example: %{ + "JSON" => %{ + "name" => "Website", + "value" => "https://pleroma.com" + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex new file mode 100644 index 000000000..a50bce5ed --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex @@ -0,0 +1,123 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.AccountAttributeField + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountUpdateCredentialsRequest", + description: "POST body for creating an account", + type: :object, + properties: %{ + bot: %Schema{ + type: :boolean, + description: "Whether the account has a bot flag." + }, + display_name: %Schema{ + type: :string, + description: "The display name to use for the profile." + }, + note: %Schema{type: :string, description: "The account bio."}, + avatar: %Schema{ + type: :string, + description: "Avatar image encoded using multipart/form-data", + format: :binary + }, + header: %Schema{ + type: :string, + description: "Header image encoded using multipart/form-data", + format: :binary + }, + locked: %Schema{ + type: :boolean, + description: "Whether manual approval of follow requests is required." + }, + fields_attributes: %Schema{ + oneOf: [%Schema{type: :array, items: AccountAttributeField}, %Schema{type: :object}] + }, + # NOTE: `source` field is not supported + # + # source: %Schema{ + # type: :object, + # properties: %{ + # privacy: %Schema{type: :string}, + # sensitive: %Schema{type: :boolean}, + # language: %Schema{type: :string} + # } + # }, + + # Pleroma-specific fields + no_rich_text: %Schema{ + type: :boolean, + description: "html tags are stripped from all statuses requested from the API" + }, + hide_followers: %Schema{type: :boolean, description: "user's followers will be hidden"}, + hide_follows: %Schema{type: :boolean, description: "user's follows will be hidden"}, + hide_followers_count: %Schema{ + type: :boolean, + description: "user's follower count will be hidden" + }, + hide_follows_count: %Schema{ + type: :boolean, + description: "user's follow count will be hidden" + }, + hide_favorites: %Schema{ + type: :boolean, + description: "user's favorites timeline will be hidden" + }, + show_role: %Schema{ + type: :boolean, + description: "user's role (e.g admin, moderator) will be exposed to anyone in the + API" + }, + default_scope: %Schema{ + type: :string, + description: "The scope returned under privacy key in Source subentity" + }, + pleroma_settings_store: %Schema{ + type: :object, + description: "Opaque user settings to be saved on the backend." + }, + skip_thread_containment: %Schema{ + type: :boolean, + description: "Skip filtering out broken threads" + }, + allow_following_move: %Schema{ + type: :boolean, + description: "Allows automatically follow moved following accounts" + }, + pleroma_background_image: %Schema{ + type: :string, + description: "Sets the background image of the user.", + format: :binary + }, + discoverable: %Schema{ + type: :boolean, + description: "Discovery of this account in search results and other services is allowed." + }, + actor_type: %Schema{type: :string, description: "the type of this account."} + }, + example: %{ + bot: false, + display_name: "cofe", + note: "foobar", + fields_attributes: [%{name: "foo", value: "bar"}], + no_rich_text: false, + hide_followers: true, + hide_follows: false, + hide_followers_count: false, + hide_follows_count: false, + hide_favorites: false, + show_role: false, + default_scope: "private", + pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}}, + skip_thread_containment: false, + allow_following_move: false, + discoverable: false, + actor_type: "Person" + } + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index eb082daf8..9c986b3b2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -82,7 +82,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OpenApiSpex.Plug.CastAndValidate, - [render_error: Pleroma.Web.ApiSpec.RenderError] when action == :create + [render_error: Pleroma.Web.ApiSpec.RenderError] + when action in [:create, :verify_credentials, :update_credentials] ) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @@ -152,9 +153,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "PATCH /api/v1/accounts/update_credentials" - def update_credentials(%{assigns: %{user: original_user}} = conn, params) do + def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do user = original_user + params = + params + |> Map.from_struct() + |> Enum.filter(fn {_, value} -> not is_nil(value) end) + |> Enum.into(%{}) + user_params = [ :no_rich_text, @@ -170,22 +177,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :discoverable ] |> Enum.reduce(%{}, fn key, acc -> - add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)}) + add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)}) end) - |> add_if_present(params, "display_name", :name) - |> add_if_present(params, "note", :bio) - |> add_if_present(params, "avatar", :avatar) - |> add_if_present(params, "header", :banner) - |> add_if_present(params, "pleroma_background_image", :background) + |> add_if_present(params, :display_name, :name) + |> add_if_present(params, :note, :bio) + |> add_if_present(params, :avatar, :avatar) + |> add_if_present(params, :header, :banner) + |> add_if_present(params, :pleroma_background_image, :background) |> add_if_present( params, - "fields_attributes", + :fields_attributes, :raw_fields, &{:ok, normalize_fields_attributes(&1)} ) - |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store) - |> add_if_present(params, "default_scope", :default_scope) - |> add_if_present(params, "actor_type", :actor_type) + |> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store) + |> add_if_present(params, :default_scope, :default_scope) + |> add_if_present(params, :actor_type, :actor_type) changeset = User.update_changeset(user, user_params) @@ -200,7 +207,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do with true <- Map.has_key?(params, params_field), - {:ok, new_value} <- value_function.(params[params_field]) do + {:ok, new_value} <- value_function.(Map.get(params, params_field)) do Map.put(map, map_field, new_value) else _ -> map @@ -211,7 +218,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do if Enum.all?(fields, &is_tuple/1) do Enum.map(fields, fn {_, v} -> v end) else - fields + Enum.map(fields, fn + %Pleroma.Web.ApiSpec.Schemas.AccountAttributeField{} = field -> + %{"name" => field.name, "value" => field.value} + + field -> + field + end) end end -- cgit v1.2.3 From ab400b2ddb205271b0a2680c45db18844f59a27d Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 7 Apr 2020 16:18:23 +0400 Subject: Add specs for ActorType and VisibilityScope --- lib/pleroma/web/api_spec/schemas/account.ex | 6 ++++-- .../api_spec/schemas/account_update_credentials_request.ex | 9 ++++----- lib/pleroma/web/api_spec/schemas/actor_type.ex | 13 +++++++++++++ lib/pleroma/web/api_spec/schemas/visibility_scope.ex | 14 ++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/actor_type.ex create mode 100644 lib/pleroma/web/api_spec/schemas/visibility_scope.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index 59c4ac4a4..beb093182 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji alias Pleroma.Web.ApiSpec.Schemas.AccountField + alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope require OpenApiSpex @@ -87,12 +89,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do properties: %{ fields: %Schema{type: :array, items: AccountField}, note: %Schema{type: :string}, - privacy: %Schema{type: :string}, + privacy: VisibilityScope, sensitive: %Schema{type: :boolean}, pleroma: %Schema{ type: :object, properties: %{ - actor_type: %Schema{type: :string}, + actor_type: ActorType, discoverable: %Schema{type: :boolean}, no_rich_text: %Schema{type: :boolean}, show_role: %Schema{type: :boolean} diff --git a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex index a50bce5ed..6ab48193e 100644 --- a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex +++ b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex @@ -5,6 +5,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.AccountAttributeField + alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope require OpenApiSpex OpenApiSpex.schema(%{ @@ -73,10 +75,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do description: "user's role (e.g admin, moderator) will be exposed to anyone in the API" }, - default_scope: %Schema{ - type: :string, - description: "The scope returned under privacy key in Source subentity" - }, + default_scope: VisibilityScope, pleroma_settings_store: %Schema{ type: :object, description: "Opaque user settings to be saved on the backend." @@ -98,7 +97,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do type: :boolean, description: "Discovery of this account in search results and other services is allowed." }, - actor_type: %Schema{type: :string, description: "the type of this account."} + actor_type: ActorType }, example: %{ bot: false, diff --git a/lib/pleroma/web/api_spec/schemas/actor_type.ex b/lib/pleroma/web/api_spec/schemas/actor_type.ex new file mode 100644 index 000000000..ac9b46678 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/actor_type.ex @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.ActorType do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "ActorType", + type: :string, + enum: ["Application", "Group", "Organization", "Person", "Service"] + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex new file mode 100644 index 000000000..8c81a4d73 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex @@ -0,0 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "VisibilityScope", + description: "Status visibility", + type: :string, + enum: ["public", "unlisted", "private", "direct"] + }) +end -- cgit v1.2.3 From d7d6a83233f24b80005b4f49a8697535620e4b83 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 7 Apr 2020 18:29:05 +0400 Subject: Add spec for AccountController.relationships --- .../web/api_spec/operations/account_operation.ex | 24 +++++++++- .../schemas/account_relationship_response.ex | 43 +++++++++++++++++ .../schemas/account_relationships_response.ex | 55 ++++++++++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 4 +- 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account_relationship_response.ex create mode 100644 lib/pleroma/web/api_spec/schemas/account_relationships_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index d7b56cc2b..352f66e9d 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -4,10 +4,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Operation + alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest @spec open_api_operation(atom) :: Operation.t() @@ -60,7 +62,27 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def relationships_operation do - :ok + %Operation{ + tags: ["accounts"], + summary: "Check relationships to other accounts", + operationId: "AccountController.relationships", + description: "Find out whether a given account is followed, blocked, muted, etc.", + security: [%{"oAuth" => ["read:follows"]}], + parameters: [ + Operation.parameter( + :id, + :query, + %Schema{ + oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}] + }, + "Account IDs", + example: "123" + ) + ], + responses: %{ + 200 => Operation.response("Account", "application/json", AccountRelationshipsResponse) + } + } end def show_operation do diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex b/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex new file mode 100644 index 000000000..9974b946b --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipResponse do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountRelationshipResponse", + description: "Response schema for an account relationship", + type: :object, + properties: %{ + id: %Schema{type: :string}, + following: %Schema{type: :boolean}, + showing_reblogs: %Schema{type: :boolean}, + followed_by: %Schema{type: :boolean}, + blocking: %Schema{type: :boolean}, + blocked_by: %Schema{type: :boolean}, + muting: %Schema{type: :boolean}, + muting_notifications: %Schema{type: :boolean}, + requested: %Schema{type: :boolean}, + domain_blocking: %Schema{type: :boolean}, + endorsed: %Schema{type: :boolean} + }, + example: %{ + "JSON" => %{ + "id" => "1", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => false, + "blocked_by" => false, + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "domain_blocking" => false, + "endorsed" => false + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex b/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex new file mode 100644 index 000000000..2ca632310 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountRelationshipsResponse", + description: "Response schema for account relationships", + type: :array, + items: Pleroma.Web.ApiSpec.Schemas.AccountRelationshipResponse, + example: [ + %{ + "id" => "1", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => false, + "blocked_by" => true, + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "domain_blocking" => false, + "endorsed" => true + }, + %{ + "id" => "2", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => false, + "blocked_by" => true, + "muting" => true, + "muting_notifications" => false, + "requested" => true, + "domain_blocking" => false, + "endorsed" => false + }, + %{ + "id" => "3", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => true, + "blocked_by" => false, + "muting" => true, + "muting_notifications" => false, + "requested" => false, + "domain_blocking" => true, + "endorsed" => false + } + ] + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 9c986b3b2..1652e3a1b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -83,7 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OpenApiSpex.Plug.CastAndValidate, [render_error: Pleroma.Web.ApiSpec.RenderError] - when action in [:create, :verify_credentials, :update_credentials] + when action in [:create, :verify_credentials, :update_credentials, :relationships] ) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @@ -229,7 +229,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/relationships" - def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def relationships(%{assigns: %{user: user}} = conn, %{id: id}) do targets = User.get_all_by_ids(List.wrap(id)) render(conn, "relationships.json", user: user, targets: targets) -- cgit v1.2.3 From 278b3fa0ad0ca58a9e5549e98d24944bbe0bf766 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 7 Apr 2020 18:53:12 +0400 Subject: Add spec for AccountController.show --- lib/pleroma/web/api_spec/operations/account_operation.ex | 16 +++++++++++++++- .../web/mastodon_api/controllers/account_controller.ex | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 352f66e9d..5b1b2eb4c 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -86,7 +86,21 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def show_operation do - :ok + %Operation{ + tags: ["accounts"], + summary: "Account", + operationId: "AccountController.show", + description: "View information about a profile.", + parameters: [ + Operation.parameter(:id, :path, :string, "Account ID or nickname", + example: "123", + required: true + ) + ], + responses: %{ + 200 => Operation.response("Account", "application/json", Account) + } + } end def statuses_operation do diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 1652e3a1b..67375f31c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -83,7 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OpenApiSpex.Plug.CastAndValidate, [render_error: Pleroma.Web.ApiSpec.RenderError] - when action in [:create, :verify_credentials, :update_credentials, :relationships] + when action in [:create, :verify_credentials, :update_credentials, :relationships, :show] ) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @@ -239,7 +239,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, []) @doc "GET /api/v1/accounts/:id" - def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do + def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), true <- User.visible_for?(user, for_user) do render(conn, "show.json", user: user, for: for_user) -- cgit v1.2.3 From 03124c96cc192ef8c4893738a0cee552c6984da6 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 8 Apr 2020 22:33:25 +0400 Subject: Add spec for AccountController.statuses --- lib/pleroma/web/activity_pub/activity_pub.ex | 11 +- lib/pleroma/web/api_spec.ex | 8 + .../web/api_spec/operations/account_operation.ex | 45 +++- .../schemas/account_update_credentials_request.ex | 5 +- lib/pleroma/web/api_spec/schemas/boolean_like.ex | 36 ++++ lib/pleroma/web/api_spec/schemas/poll.ex | 35 ++++ lib/pleroma/web/api_spec/schemas/status.ex | 227 +++++++++++++++++++++ .../web/api_spec/schemas/statuses_response.ex | 13 ++ .../mastodon_api/controllers/account_controller.ex | 17 +- lib/pleroma/web/mastodon_api/views/status_view.ex | 8 +- 10 files changed, 383 insertions(+), 22 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/boolean_like.ex create mode 100644 lib/pleroma/web/api_spec/schemas/poll.ex create mode 100644 lib/pleroma/web/api_spec/schemas/status.ex create mode 100644 lib/pleroma/web/api_spec/schemas/statuses_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 86b105b7f..1909ce097 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -853,7 +853,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp exclude_visibility(query, %{"exclude_visibilities" => visibility}) - when visibility not in @valid_visibilities do + when visibility not in [nil | @valid_visibilities] do Logger.error("Could not exclude visibility to #{visibility}") query end @@ -1060,7 +1060,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do raise "Can't use the child object without preloading!" end - defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do + defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do from( [_activity, object] in query, where: fragment("not (?)->'attachment' = (?)", object.data, ^[]) @@ -1069,7 +1069,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_media(query, _), do: query - defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do + defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do from( [_activity, object] in query, where: fragment("?->>'inReplyTo' is null", object.data) @@ -1078,7 +1078,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_replies(query, _), do: query - defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do + defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data)) end @@ -1157,7 +1157,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end - defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do + defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids}) + when pinned in [true, "true", "1"] do from(activity in query, where: activity.id in ^ids) end diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index c85fe30d1..d11e776d0 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ApiSpec do alias OpenApiSpex.OpenApi + alias OpenApiSpex.Operation alias Pleroma.Web.Endpoint alias Pleroma.Web.Router @@ -24,6 +25,13 @@ defmodule Pleroma.Web.ApiSpec do # populate the paths from a phoenix router paths: OpenApiSpex.Paths.from_router(Router), components: %OpenApiSpex.Components{ + parameters: %{ + "accountIdOrNickname" => + Operation.parameter(:id, :path, :string, "Account ID or nickname", + example: "123", + required: true + ) + }, securitySchemes: %{ "oAuth" => %OpenApiSpex.SecurityScheme{ type: "oauth2", diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 5b1b2eb4c..09e6d24ed 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Operation + alias OpenApiSpex.Reference alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers alias Pleroma.Web.ApiSpec.Schemas.Account @@ -11,6 +12,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.StatusesResponse + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -91,12 +95,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do summary: "Account", operationId: "AccountController.show", description: "View information about a profile.", - parameters: [ - Operation.parameter(:id, :path, :string, "Account ID or nickname", - example: "123", - required: true - ) - ], + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], responses: %{ 200 => Operation.response("Account", "application/json", Account) } @@ -104,7 +103,39 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def statuses_operation do - :ok + %Operation{ + tags: ["accounts"], + summary: "Statuses", + operationId: "AccountController.statuses", + description: + "Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter(:pinned, :query, BooleanLike, "Pinned"), + Operation.parameter(:tagged, :query, :string, "With tag"), + Operation.parameter(:only_media, :query, BooleanLike, "Only meadia"), + Operation.parameter(:with_muted, :query, BooleanLike, "With muted"), + Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblobs"), + Operation.parameter( + :exclude_visibilities, + :query, + %Schema{type: :array, items: VisibilityScope}, + "Exclude visibilities" + ), + Operation.parameter(:max_id, :query, :string, "Max ID"), + Operation.parameter(:min_id, :query, :string, "Mix ID"), + Operation.parameter(:since_id, :query, :string, "Since ID"), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 20, maximum: 40}, + "Limit" + ) + ], + responses: %{ + 200 => Operation.response("Statuses", "application/json", StatusesResponse) + } + } end def followers_operation do diff --git a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex index 6ab48193e..35220c78a 100644 --- a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex +++ b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex @@ -38,7 +38,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do description: "Whether manual approval of follow requests is required." }, fields_attributes: %Schema{ - oneOf: [%Schema{type: :array, items: AccountAttributeField}, %Schema{type: :object}] + oneOf: [ + %Schema{type: :array, items: AccountAttributeField}, + %Schema{type: :object, additionalProperties: %Schema{type: AccountAttributeField}} + ] }, # NOTE: `source` field is not supported # diff --git a/lib/pleroma/web/api_spec/schemas/boolean_like.ex b/lib/pleroma/web/api_spec/schemas/boolean_like.ex new file mode 100644 index 000000000..f3bfb74da --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/boolean_like.ex @@ -0,0 +1,36 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "BooleanLike", + description: """ + The following values will be treated as `false`: + - false + - 0 + - "0", + - "f", + - "F", + - "false", + - "FALSE", + - "off", + - "OFF" + + All other non-null values will be treated as `true` + """, + anyOf: [ + %Schema{type: :boolean}, + %Schema{type: :string}, + %Schema{type: :integer} + ] + }) + + def after_cast(value, _schmea) do + {:ok, Pleroma.Web.ControllerHelper.truthy_param?(value)} + end +end diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex new file mode 100644 index 000000000..2a9975f85 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Poll do + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Poll", + description: "Response schema for account custom fields", + type: :object, + properties: %{ + id: %Schema{type: :string}, + expires_at: %Schema{type: :string, format: "date-time"}, + expired: %Schema{type: :boolean}, + multiple: %Schema{type: :boolean}, + votes_count: %Schema{type: :integer}, + voted: %Schema{type: :boolean}, + emojis: %Schema{type: :array, items: AccountEmoji}, + options: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + title: %Schema{type: :string}, + votes_count: %Schema{type: :integer} + } + } + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex new file mode 100644 index 000000000..486c3a0fe --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -0,0 +1,227 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Status do + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.Account + alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.Poll + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Status", + description: "Response schema for a status", + type: :object, + properties: %{ + account: Account, + application: %Schema{ + type: :object, + properties: %{ + name: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + } + }, + bookmarked: %Schema{type: :boolean}, + card: %Schema{ + type: :object, + nullable: true, + properties: %{ + type: %Schema{type: :string}, + provider_name: %Schema{type: :string}, + provider_url: %Schema{type: :string}, + url: %Schema{type: :string}, + image: %Schema{type: :string}, + title: %Schema{type: :string}, + description: %Schema{type: :string} + } + }, + content: %Schema{type: :string}, + created_at: %Schema{type: :string, format: "date-time"}, + emojis: %Schema{type: :array, items: AccountEmoji}, + favourited: %Schema{type: :boolean}, + favourites_count: %Schema{type: :integer}, + id: %Schema{type: :string}, + in_reply_to_account_id: %Schema{type: :string, nullable: true}, + in_reply_to_id: %Schema{type: :string, nullable: true}, + language: %Schema{type: :string, nullable: true}, + media_attachments: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + id: %Schema{type: :string}, + url: %Schema{type: :string}, + remote_url: %Schema{type: :string}, + preview_url: %Schema{type: :string}, + text_url: %Schema{type: :string}, + description: %Schema{type: :string}, + type: %Schema{type: :string, enum: ["image", "video", "audio", "unknown"]}, + pleroma: %Schema{ + type: :object, + properties: %{mime_type: %Schema{type: :string}} + } + } + } + }, + mentions: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + id: %Schema{type: :string}, + acct: %Schema{type: :string}, + username: %Schema{type: :string}, + url: %Schema{type: :string} + } + } + }, + muted: %Schema{type: :boolean}, + pinned: %Schema{type: :boolean}, + pleroma: %Schema{ + type: :object, + properties: %{ + content: %Schema{type: :object, additionalProperties: %Schema{type: :string}}, + conversation_id: %Schema{type: :integer}, + direct_conversation_id: %Schema{type: :string, nullable: true}, + emoji_reactions: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + name: %Schema{type: :string}, + count: %Schema{type: :integer}, + me: %Schema{type: :boolean} + } + } + }, + expires_at: %Schema{type: :string, format: "date-time", nullable: true}, + in_reply_to_account_acct: %Schema{type: :string, nullable: true}, + local: %Schema{type: :boolean}, + spoiler_text: %Schema{type: :object, additionalProperties: %Schema{type: :string}}, + thread_muted: %Schema{type: :boolean} + } + }, + poll: %Schema{type: Poll, nullable: true}, + reblog: %Schema{ + allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}], + nullable: true + }, + reblogged: %Schema{type: :boolean}, + reblogs_count: %Schema{type: :integer}, + replies_count: %Schema{type: :integer}, + sensitive: %Schema{type: :boolean}, + spoiler_text: %Schema{type: :string}, + tags: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + name: %Schema{type: :string}, + url: %Schema{type: :string} + } + } + }, + uri: %Schema{type: :string}, + url: %Schema{type: :string}, + visibility: VisibilityScope + }, + example: %{ + "JSON" => %{ + "account" => %{ + "acct" => "nick6", + "avatar" => "http://localhost:4001/images/avi.png", + "avatar_static" => "http://localhost:4001/images/avi.png", + "bot" => false, + "created_at" => "2020-04-07T19:48:51.000Z", + "display_name" => "Test テスト User 6", + "emojis" => [], + "fields" => [], + "followers_count" => 1, + "following_count" => 0, + "header" => "http://localhost:4001/images/banner.png", + "header_static" => "http://localhost:4001/images/banner.png", + "id" => "9toJCsKN7SmSf3aj5c", + "locked" => false, + "note" => "Tester Number 6", + "pleroma" => %{ + "background_image" => nil, + "confirmation_pending" => false, + "hide_favorites" => true, + "hide_followers" => false, + "hide_followers_count" => false, + "hide_follows" => false, + "hide_follows_count" => false, + "is_admin" => false, + "is_moderator" => false, + "relationship" => %{ + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => true, + "id" => "9toJCsKN7SmSf3aj5c", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false + }, + "skip_thread_containment" => false, + "tags" => [] + }, + "source" => %{ + "fields" => [], + "note" => "Tester Number 6", + "pleroma" => %{"actor_type" => "Person", "discoverable" => false}, + "sensitive" => false + }, + "statuses_count" => 1, + "url" => "http://localhost:4001/users/nick6", + "username" => "nick6" + }, + "application" => %{"name" => "Web", "website" => nil}, + "bookmarked" => false, + "card" => nil, + "content" => "foobar", + "created_at" => "2020-04-07T19:48:51.000Z", + "emojis" => [], + "favourited" => false, + "favourites_count" => 0, + "id" => "9toJCu5YZW7O7gfvH6", + "in_reply_to_account_id" => nil, + "in_reply_to_id" => nil, + "language" => nil, + "media_attachments" => [], + "mentions" => [], + "muted" => false, + "pinned" => false, + "pleroma" => %{ + "content" => %{"text/plain" => "foobar"}, + "conversation_id" => 345_972, + "direct_conversation_id" => nil, + "emoji_reactions" => [], + "expires_at" => nil, + "in_reply_to_account_acct" => nil, + "local" => true, + "spoiler_text" => %{"text/plain" => ""}, + "thread_muted" => false + }, + "poll" => nil, + "reblog" => nil, + "reblogged" => false, + "reblogs_count" => 0, + "replies_count" => 0, + "sensitive" => false, + "spoiler_text" => "", + "tags" => [], + "uri" => "http://localhost:4001/objects/0f5dad44-0e9e-4610-b377-a2631e499190", + "url" => "http://localhost:4001/notice/9toJCu5YZW7O7gfvH6", + "visibility" => "private" + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/statuses_response.ex b/lib/pleroma/web/api_spec/schemas/statuses_response.ex new file mode 100644 index 000000000..fb7c7e0aa --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/statuses_response.ex @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.StatusesResponse do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "StatusesResponse", + type: :array, + items: Pleroma.Web.ApiSpec.Schemas.Status + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 67375f31c..208df5698 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -83,7 +83,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OpenApiSpex.Plug.CastAndValidate, [render_error: Pleroma.Web.ApiSpec.RenderError] - when action in [:create, :verify_credentials, :update_credentials, :relationships, :show] + when action in [ + :create, + :verify_credentials, + :update_credentials, + :relationships, + :show, + :statuses + ] ) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @@ -250,12 +257,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/accounts/:id/statuses" def statuses(%{assigns: %{user: reading_user}} = conn, params) do - with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user), + with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user), true <- User.visible_for?(user, reading_user) do params = params - |> Map.put("tag", params["tagged"]) - |> Map.delete("godmode") + |> Map.delete(:tagged) + |> Enum.filter(&(not is_nil(&1))) + |> Map.new(fn {key, value} -> {to_string(key), value} end) + |> Map.put("tag", params[:tagged]) activities = ActivityPub.fetch_user_activities(user, reading_user, params) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index b5850e1ae..ba40fd63e 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -521,11 +521,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do """ @spec build_tags(list(any())) :: list(map()) def build_tags(object_tags) when is_list(object_tags) do - object_tags = for tag when is_binary(tag) <- object_tags, do: tag - - Enum.reduce(object_tags, [], fn tag, tags -> - tags ++ [%{name: tag, url: "/tag/#{URI.encode(tag)}"}] - end) + object_tags + |> Enum.filter(&is_binary/1) + |> Enum.map(&%{name: &1, url: "/tag/#{URI.encode(&1)}"}) end def build_tags(_), do: [] -- cgit v1.2.3 From bd6e2b300f82e66afb121c2339c3cbbfb0b1a446 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 8 Apr 2020 23:16:20 +0400 Subject: Add spec for AccountController.followers --- .../web/api_spec/operations/account_operation.ex | 19 ++++++++++++++++++- lib/pleroma/web/api_spec/schemas/accounts_response.ex | 13 +++++++++++++ .../mastodon_api/controllers/account_controller.ex | 8 +++++++- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/accounts_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 09e6d24ed..070c74758 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest alias Pleroma.Web.ApiSpec.Schemas.BooleanLike alias Pleroma.Web.ApiSpec.Schemas.StatusesResponse @@ -139,7 +140,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do end def followers_operation do - :ok + %Operation{ + tags: ["accounts"], + summary: "Followers", + operationId: "AccountController.followers", + security: [%{"oAuth" => ["read:accounts"]}], + description: + "Accounts which follow the given account, if network is not hidden by the account owner.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter(:max_id, :query, :string, "Max ID"), + Operation.parameter(:since_id, :query, :string, "Since ID"), + Operation.parameter(:limit, :query, :integer, "Limit") + ], + responses: %{ + 200 => Operation.response("Accounts", "application/json", AccountsResponse) + } + } end def following_operation, do: :ok diff --git a/lib/pleroma/web/api_spec/schemas/accounts_response.ex b/lib/pleroma/web/api_spec/schemas/accounts_response.ex new file mode 100644 index 000000000..b714f59e7 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/accounts_response.ex @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountsResponse do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountsResponse", + type: :array, + items: Pleroma.Web.ApiSpec.Schemas.Account + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 208df5698..1ffccdd1d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -89,7 +89,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :update_credentials, :relationships, :show, - :statuses + :statuses, + :followers ] ) @@ -284,6 +285,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/accounts/:id/followers" def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do + params = + params + |> Enum.map(fn {key, value} -> {to_string(key), value} end) + |> Enum.into(%{}) + followers = cond do for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params) -- cgit v1.2.3 From e105cc12b67e44eb4e19293b850731f300999a4f Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 8 Apr 2020 23:38:07 +0400 Subject: Add spec for AccountController.following --- .../web/api_spec/operations/account_operation.ex | 35 ++++++++++++++++++++-- .../mastodon_api/controllers/account_controller.ex | 8 ++++- 2 files changed, 40 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 070c74758..456d08a45 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -150,8 +150,40 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do parameters: [ %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, Operation.parameter(:max_id, :query, :string, "Max ID"), + Operation.parameter(:min_id, :query, :string, "Mix ID"), Operation.parameter(:since_id, :query, :string, "Since ID"), - Operation.parameter(:limit, :query, :integer, "Limit") + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 20, maximum: 40}, + "Limit" + ) + ], + responses: %{ + 200 => Operation.response("Accounts", "application/json", AccountsResponse) + } + } + end + + def following_operation do + %Operation{ + tags: ["accounts"], + summary: "Following", + operationId: "AccountController.following", + security: [%{"oAuth" => ["read:accounts"]}], + description: + "Accounts which the given account is following, if network is not hidden by the account owner.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter(:max_id, :query, :string, "Max ID"), + Operation.parameter(:min_id, :query, :string, "Mix ID"), + Operation.parameter(:since_id, :query, :string, "Since ID"), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 20, maximum: 40}, + "Limit" + ) ], responses: %{ 200 => Operation.response("Accounts", "application/json", AccountsResponse) @@ -159,7 +191,6 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def following_operation, do: :ok def lists_operation, do: :ok def follow_operation, do: :ok def unfollow_operation, do: :ok diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 1ffccdd1d..e74180662 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -90,7 +90,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :relationships, :show, :statuses, - :followers + :followers, + :following ] ) @@ -304,6 +305,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/accounts/:id/following" def following(%{assigns: %{user: for_user, account: user}} = conn, params) do + params = + params + |> Enum.map(fn {key, value} -> {to_string(key), value} end) + |> Enum.into(%{}) + followers = cond do for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params) -- cgit v1.2.3 From 1b680a98ae15035215fa8489f825af72532340c4 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 8 Apr 2020 23:51:46 +0400 Subject: Add spec for AccountController.lists --- .../web/api_spec/operations/account_operation.ex | 18 +++++++++++++++- lib/pleroma/web/api_spec/schemas/list.ex | 25 ++++++++++++++++++++++ lib/pleroma/web/api_spec/schemas/lists_response.ex | 16 ++++++++++++++ .../mastodon_api/controllers/account_controller.ex | 3 ++- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/list.ex create mode 100644 lib/pleroma/web/api_spec/schemas/lists_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 456d08a45..ad10f4ec9 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.AccountsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.ListsResponse alias Pleroma.Web.ApiSpec.Schemas.StatusesResponse alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -191,7 +192,22 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def lists_operation, do: :ok + def lists_operation do + %Operation{ + tags: ["accounts"], + summary: "Lists containing this account", + operationId: "AccountController.lists", + security: [%{"oAuth" => ["read:lists"]}], + description: "User lists that you have added this account to.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"} + ], + responses: %{ + 200 => Operation.response("Lists", "application/json", ListsResponse) + } + } + end + def follow_operation, do: :ok def unfollow_operation, do: :ok def mute_operation, do: :ok diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex new file mode 100644 index 000000000..30fa7db93 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/list.ex @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.List do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "List", + description: "Response schema for a list", + type: :object, + properties: %{ + id: %Schema{type: :string}, + title: %Schema{type: :string} + }, + example: %{ + "JSON" => %{ + "id" => "123", + "title" => "my list" + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/lists_response.ex b/lib/pleroma/web/api_spec/schemas/lists_response.ex new file mode 100644 index 000000000..132454579 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/lists_response.ex @@ -0,0 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.ListsResponse do + alias Pleroma.Web.ApiSpec.Schemas.List + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "ListsResponse", + description: "Response schema for lists", + type: :array, + items: List + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index e74180662..2c5cd8cde 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -91,7 +91,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :show, :statuses, :followers, - :following + :following, + :lists ] ) -- cgit v1.2.3 From 854780c72bc90a55d7005a05861d45771c5aeadf Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 15:25:24 +0400 Subject: Add spec for AccountController.follow --- lib/pleroma/web/api_spec.ex | 2 +- .../web/api_spec/operations/account_operation.ex | 28 +++++++++++--- .../web/api_spec/schemas/account_relationship.ex | 45 ++++++++++++++++++++++ .../schemas/account_relationship_response.ex | 43 --------------------- .../schemas/account_relationships_response.ex | 5 ++- .../mastodon_api/controllers/account_controller.ex | 7 ++-- 6 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account_relationship.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_relationship_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index d11e776d0..b3c1e3ea2 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -39,7 +39,7 @@ defmodule Pleroma.Web.ApiSpec do password: %OpenApiSpex.OAuthFlow{ authorizationUrl: "/oauth/authorize", tokenUrl: "/oauth/token", - scopes: %{"read" => "read", "write" => "write"} + scopes: %{"read" => "read", "write" => "write", "follow" => "follow"} } } } diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index ad10f4ec9..a76141f7a 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest @@ -186,9 +187,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Limit" ) ], - responses: %{ - 200 => Operation.response("Accounts", "application/json", AccountsResponse) - } + responses: %{200 => Operation.response("Accounts", "application/json", AccountsResponse)} } end @@ -199,16 +198,33 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do operationId: "AccountController.lists", security: [%{"oAuth" => ["read:lists"]}], description: "User lists that you have added this account to.", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{200 => Operation.response("Lists", "application/json", ListsResponse)} + } + end + + def follow_operation do + %Operation{ + tags: ["accounts"], + summary: "Follow", + operationId: "AccountController.follow", + security: [%{"oAuth" => ["follow", "write:follows"]}], + description: "Follow the given account", parameters: [ - %Reference{"$ref": "#/components/parameters/accountIdOrNickname"} + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :reblogs, + :query, + BooleanLike, + "Receive this account's reblogs in home timeline? Defaults to true." + ) ], responses: %{ - 200 => Operation.response("Lists", "application/json", ListsResponse) + 200 => Operation.response("Relationship", "application/json", AccountRelationship) } } end - def follow_operation, do: :ok def unfollow_operation, do: :ok def mute_operation, do: :ok def unmute_operation, do: :ok diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex new file mode 100644 index 000000000..7db3b49bb --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountRelationship", + description: "Response schema for relationship", + type: :object, + properties: %{ + blocked_by: %Schema{type: :boolean}, + blocking: %Schema{type: :boolean}, + domain_blocking: %Schema{type: :boolean}, + endorsed: %Schema{type: :boolean}, + followed_by: %Schema{type: :boolean}, + following: %Schema{type: :boolean}, + id: %Schema{type: :string}, + muting: %Schema{type: :boolean}, + muting_notifications: %Schema{type: :boolean}, + requested: %Schema{type: :boolean}, + showing_reblogs: %Schema{type: :boolean}, + subscribing: %Schema{type: :boolean} + }, + example: %{ + "JSON" => %{ + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => false, + "id" => "9tKi3esbG7OQgZ2920", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex b/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex deleted file mode 100644 index 9974b946b..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_relationship_response.ex +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipResponse do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountRelationshipResponse", - description: "Response schema for an account relationship", - type: :object, - properties: %{ - id: %Schema{type: :string}, - following: %Schema{type: :boolean}, - showing_reblogs: %Schema{type: :boolean}, - followed_by: %Schema{type: :boolean}, - blocking: %Schema{type: :boolean}, - blocked_by: %Schema{type: :boolean}, - muting: %Schema{type: :boolean}, - muting_notifications: %Schema{type: :boolean}, - requested: %Schema{type: :boolean}, - domain_blocking: %Schema{type: :boolean}, - endorsed: %Schema{type: :boolean} - }, - example: %{ - "JSON" => %{ - "id" => "1", - "following" => true, - "showing_reblogs" => true, - "followed_by" => true, - "blocking" => false, - "blocked_by" => false, - "muting" => false, - "muting_notifications" => false, - "requested" => false, - "domain_blocking" => false, - "endorsed" => false - } - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex b/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex index 2ca632310..960e14db1 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do title: "AccountRelationshipsResponse", description: "Response schema for account relationships", type: :array, - items: Pleroma.Web.ApiSpec.Schemas.AccountRelationshipResponse, + items: Pleroma.Web.ApiSpec.Schemas.AccountRelationship, example: [ %{ "id" => "1", @@ -22,6 +22,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do "muting_notifications" => false, "requested" => false, "domain_blocking" => false, + "subscribing" => false, "endorsed" => true }, %{ @@ -35,6 +36,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do "muting_notifications" => false, "requested" => true, "domain_blocking" => false, + "subscribing" => false, "endorsed" => false }, %{ @@ -48,6 +50,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do "muting_notifications" => false, "requested" => false, "domain_blocking" => true, + "subscribing" => true, "endorsed" => false } ] diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 2c5cd8cde..d2ad65ef3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -92,7 +92,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :statuses, :followers, :following, - :lists + :lists, + :follow ] ) @@ -337,8 +338,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:error, :not_found} end - def follow(%{assigns: %{user: follower, account: followed}} = conn, _params) do - with {:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do + def follow(%{assigns: %{user: follower, account: followed}} = conn, params) do + with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do render(conn, "relationship.json", user: follower, target: followed) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) -- cgit v1.2.3 From aa958a6dda7cdcf12e9cd9232e7c6be421610317 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 17:57:21 +0400 Subject: Add spec for AccountController.unfollow --- lib/pleroma/web/api_spec/operations/account_operation.ex | 15 ++++++++++++++- .../web/mastodon_api/controllers/account_controller.ex | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index a76141f7a..8925ebefd 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -225,7 +225,20 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def unfollow_operation, do: :ok + def unfollow_operation do + %Operation{ + tags: ["accounts"], + summary: "Unfollow", + operationId: "AccountController.unfollow", + security: [%{"oAuth" => ["follow", "write:follows"]}], + description: "Unfollow the given account", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def mute_operation, do: :ok def unmute_operation, do: :ok def block_operation, do: :ok diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index d2ad65ef3..1ecce2928 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -93,7 +93,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :followers, :following, :lists, - :follow + :follow, + :unfollow ] ) -- cgit v1.2.3 From e4195d4a684908d58482f9c865375a080e7b78bc Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 18:28:14 +0400 Subject: Add specs for AccountController.mute and AccountController.unmute --- .../web/api_spec/operations/account_operation.ex | 41 ++++++++++++++++++++-- .../web/api_spec/schemas/account_mute_request.ex | 24 +++++++++++++ .../mastodon_api/controllers/account_controller.ex | 10 +++--- 3 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account_mute_request.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 8925ebefd..62ae2eead 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountMuteRequest alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse alias Pleroma.Web.ApiSpec.Schemas.AccountsResponse @@ -239,8 +240,44 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def mute_operation, do: :ok - def unmute_operation, do: :ok + def mute_operation do + %Operation{ + tags: ["accounts"], + summary: "Mute", + operationId: "AccountController.mute", + security: [%{"oAuth" => ["follow", "write:mutes"]}], + requestBody: Helpers.request_body("Parameters", AccountMuteRequest), + description: + "Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :notifications, + :query, + %Schema{allOf: [BooleanLike], default: true}, + "Mute notifications in addition to statuses? Defaults to `true`." + ) + ], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + + def unmute_operation do + %Operation{ + tags: ["accounts"], + summary: "Unmute", + operationId: "AccountController.unmute", + security: [%{"oAuth" => ["follow", "write:mutes"]}], + description: "Unmute the given account.", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def block_operation, do: :ok def unblock_operation, do: :ok def follows_operation, do: :ok diff --git a/lib/pleroma/web/api_spec/schemas/account_mute_request.ex b/lib/pleroma/web/api_spec/schemas/account_mute_request.ex new file mode 100644 index 000000000..a61f6d04c --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_mute_request.ex @@ -0,0 +1,24 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountMuteRequest do + alias OpenApiSpex.Schema + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountMuteRequest", + description: "POST body for muting an account", + type: :object, + properties: %{ + notifications: %Schema{ + type: :boolean, + description: "Mute notifications in addition to statuses? Defaults to true.", + default: true + } + }, + example: %{ + "notifications" => true + } + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 1ecce2928..9aba2e094 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -94,7 +94,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :following, :lists, :follow, - :unfollow + :unfollow, + :mute, + :unmute ] ) @@ -359,10 +361,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/accounts/:id/mute" - def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do - notifications? = params |> Map.get("notifications", true) |> truthy_param?() - - with {:ok, _user_relationships} <- User.mute(muter, muted, notifications?) do + def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do + with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do render(conn, "relationship.json", user: muter, target: muted) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) -- cgit v1.2.3 From 68a979b8243b9a5b685df2c13388a93b9ede1900 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 18:41:18 +0400 Subject: Add specs for AccountController.block and AccountController.unblock --- .../web/api_spec/operations/account_operation.ex | 31 ++++++++++++++++++++-- .../mastodon_api/controllers/account_controller.ex | 4 ++- 2 files changed, 32 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 62ae2eead..73fbe8785 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -278,8 +278,35 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def block_operation, do: :ok - def unblock_operation, do: :ok + def block_operation do + %Operation{ + tags: ["accounts"], + summary: "Block", + operationId: "AccountController.block", + security: [%{"oAuth" => ["follow", "write:blocks"]}], + description: + "Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + + def unblock_operation do + %Operation{ + tags: ["accounts"], + summary: "Unblock", + operationId: "AccountController.unblock", + security: [%{"oAuth" => ["follow", "write:blocks"]}], + description: "Unblock the given account.", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def follows_operation, do: :ok def mutes_operation, do: :ok def blocks_operation, do: :ok diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 9aba2e094..c1f70f32c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -96,7 +96,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :follow, :unfollow, :mute, - :unmute + :unmute, + :block, + :unblock ] ) -- cgit v1.2.3 From ab185d3ea47deb38128dc501acdf27c47c542de2 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 20:12:09 +0400 Subject: Add spec for AccountController.follows --- .../web/api_spec/operations/account_operation.ex | 15 ++++++++++++++- .../web/api_spec/schemas/account_follows_request.ex | 18 ++++++++++++++++++ .../web/mastodon_api/controllers/account_controller.ex | 5 +++-- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/account_follows_request.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 73fbe8785..9fef7ece1 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse + alias Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest alias Pleroma.Web.ApiSpec.Schemas.AccountMuteRequest alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse @@ -307,7 +308,19 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def follows_operation, do: :ok + def follows_operation do + %Operation{ + tags: ["accounts"], + summary: "Follows", + operationId: "AccountController.follows", + security: [%{"oAuth" => ["follow", "write:follows"]}], + requestBody: Helpers.request_body("Parameters", AccountFollowsRequest, required: true), + responses: %{ + 200 => Operation.response("Account", "application/json", Account) + } + } + end + def mutes_operation, do: :ok def blocks_operation, do: :ok def endorsements_operation, do: :ok diff --git a/lib/pleroma/web/api_spec/schemas/account_follows_request.ex b/lib/pleroma/web/api_spec/schemas/account_follows_request.ex new file mode 100644 index 000000000..4fbe615d6 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/account_follows_request.ex @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest do + alias OpenApiSpex.Schema + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "AccountFollowsRequest", + description: "POST body for muting an account", + type: :object, + properties: %{ + uri: %Schema{type: :string} + }, + required: [:uri] + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index c1f70f32c..4340b9c84 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -98,7 +98,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :mute, :unmute, :block, - :unblock + :unblock, + :follows ] ) @@ -401,7 +402,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/follows" - def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do + def follows(%{assigns: %{user: follower}, body_params: %{uri: uri}} = conn, _) do with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, {_, true} <- {:followed, follower.id != followed.id}, {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do -- cgit v1.2.3 From 7e0b42d99f3eb9520bc29cc29c06512c55183482 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 9 Apr 2020 20:34:21 +0400 Subject: Add specs for AccountController.mutes, AccountController.blocks, AccountController.mutes, AccountController.endorsements --- .../web/api_spec/operations/account_operation.ex | 41 ++++++++++++++++++++-- .../mastodon_api/controllers/account_controller.ex | 23 +----------- 2 files changed, 39 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 9fef7ece1..9749c3b60 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -321,7 +321,42 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def mutes_operation, do: :ok - def blocks_operation, do: :ok - def endorsements_operation, do: :ok + def mutes_operation do + %Operation{ + tags: ["accounts"], + summary: "Muted accounts", + operationId: "AccountController.mutes", + description: "Accounts the user has muted.", + security: [%{"oAuth" => ["follow", "read:mutes"]}], + responses: %{ + 200 => Operation.response("Accounts", "application/json", AccountsResponse) + } + } + end + + def blocks_operation do + %Operation{ + tags: ["accounts"], + summary: "Blocked users", + operationId: "AccountController.blocks", + description: "View your blocks. See also accounts/:id/{block,unblock}", + security: [%{"oAuth" => ["read:blocks"]}], + responses: %{ + 200 => Operation.response("Accounts", "application/json", AccountsResponse) + } + } + end + + def endorsements_operation do + %Operation{ + tags: ["accounts"], + summary: "Endorsements", + operationId: "AccountController.endorsements", + description: "Not implemented", + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => Operation.response("Empry array", "application/json", %Schema{type: :array}) + } + } + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 4340b9c84..f72c91c51 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -80,28 +80,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(RateLimiter, [name: :app_account_creation] when action == :create) plug(:assign_account_by_id when action in @needs_account) - plug( - OpenApiSpex.Plug.CastAndValidate, - [render_error: Pleroma.Web.ApiSpec.RenderError] - when action in [ - :create, - :verify_credentials, - :update_credentials, - :relationships, - :show, - :statuses, - :followers, - :following, - :lists, - :follow, - :unfollow, - :mute, - :unmute, - :block, - :unblock, - :follows - ] - ) + plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) -- cgit v1.2.3 From c28aaf9d82a781508eba886bd455767a110d1b7c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 13 Apr 2020 21:21:04 +0400 Subject: Add OpenAPI spec for CustomEmojiController --- .../api_spec/operations/custom_emoji_operation.ex | 25 +++++++++++++ lib/pleroma/web/api_spec/schemas/custom_emoji.ex | 30 ++++++++++++++++ .../web/api_spec/schemas/custom_emojis_response.ex | 42 ++++++++++++++++++++++ .../controllers/custom_emoji_controller.ex | 4 +++ 4 files changed, 101 insertions(+) create mode 100644 lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex create mode 100644 lib/pleroma/web/api_spec/schemas/custom_emoji.ex create mode 100644 lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex new file mode 100644 index 000000000..cf2215823 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["custom_emojis"], + summary: "List custom custom emojis", + description: "Returns custom emojis that are available on the server.", + operationId: "CustomEmojiController.index", + responses: %{ + 200 => Operation.response("Custom Emojis", "application/json", CustomEmojisResponse) + } + } + end +end diff --git a/lib/pleroma/web/api_spec/schemas/custom_emoji.ex b/lib/pleroma/web/api_spec/schemas/custom_emoji.ex new file mode 100644 index 000000000..5531b2081 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/custom_emoji.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.CustomEmoji do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "CustomEmoji", + description: "Response schema for an CustomEmoji", + type: :object, + properties: %{ + shortcode: %Schema{type: :string}, + url: %Schema{type: :string}, + static_url: %Schema{type: :string}, + visible_in_picker: %Schema{type: :boolean}, + category: %Schema{type: :string}, + tags: %Schema{type: :array} + }, + example: %{ + "shortcode" => "aaaa", + "url" => "https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png", + "static_url" => + "https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png", + "visible_in_picker" => true + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex b/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex new file mode 100644 index 000000000..01582a63d --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse do + alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "CustomEmojisResponse", + description: "Response schema for custom emojis", + type: :array, + items: CustomEmoji, + example: [ + %{ + "category" => "Fun", + "shortcode" => "blank", + "static_url" => "https://lain.com/emoji/blank.png", + "tags" => ["Fun"], + "url" => "https://lain.com/emoji/blank.png", + "visible_in_picker" => true + }, + %{ + "category" => "Gif,Fun", + "shortcode" => "firefox", + "static_url" => "https://lain.com/emoji/Firefox.gif", + "tags" => ["Gif", "Fun"], + "url" => "https://lain.com/emoji/Firefox.gif", + "visible_in_picker" => true + }, + %{ + "category" => "pack:mixed", + "shortcode" => "sadcat", + "static_url" => "https://lain.com/emoji/mixed/sadcat.png", + "tags" => ["pack:mixed"], + "url" => "https://lain.com/emoji/mixed/sadcat.png", + "visible_in_picker" => true + } + ] + }) +end diff --git a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex index d82de1db5..3bfebef8b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex @@ -5,6 +5,10 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do use Pleroma.Web, :controller + plug(OpenApiSpex.Plug.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation + def index(conn, _params) do render(conn, "index.json", custom_emojis: Pleroma.Emoji.get_all()) end -- cgit v1.2.3 From f7e623c11c4b6f4f323a4317e9489092be73f9cd Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 14 Apr 2020 20:19:08 +0300 Subject: [#1364] Resolved merge conflicts with `develop`. --- lib/pleroma/notification.ex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index f517282f7..b76dd176c 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -7,7 +7,6 @@ defmodule Pleroma.Notification do alias Pleroma.Activity alias Pleroma.FollowingRelationship - alias Pleroma.Marker alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Pagination -- cgit v1.2.3 From d3e876aeeebfcdd2821ef8310bd60b785e6df560 Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:26:44 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 9749c3b60..7ead44197 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -120,7 +120,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do Operation.parameter(:tagged, :query, :string, "With tag"), Operation.parameter(:only_media, :query, BooleanLike, "Only meadia"), Operation.parameter(:with_muted, :query, BooleanLike, "With muted"), - Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblobs"), + Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), + Operation.parameter( :exclude_visibilities, :query, -- cgit v1.2.3 From a7feca1604fe7f22d10c0fd3284f14eae8609852 Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:26:53 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 7ead44197..1c726a612 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -119,7 +119,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do Operation.parameter(:pinned, :query, BooleanLike, "Pinned"), Operation.parameter(:tagged, :query, :string, "With tag"), Operation.parameter(:only_media, :query, BooleanLike, "Only meadia"), - Operation.parameter(:with_muted, :query, BooleanLike, "With muted"), + Operation.parameter(:with_muted, :query, BooleanLike, "Include statuses from muted acccounts."), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), Operation.parameter( -- cgit v1.2.3 From a794ba655f5a0a8b5512ad718601e5a03b9aebef Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:27:01 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 1c726a612..6ce2cfe25 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -118,7 +118,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, Operation.parameter(:pinned, :query, BooleanLike, "Pinned"), Operation.parameter(:tagged, :query, :string, "With tag"), - Operation.parameter(:only_media, :query, BooleanLike, "Only meadia"), + Operation.parameter(:only_media, :query, BooleanLike, "Include only statuses with media attached"), Operation.parameter(:with_muted, :query, BooleanLike, "Include statuses from muted acccounts."), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), -- cgit v1.2.3 From bfa26b09370ee049f8d70c4112709f2666c590d1 Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:30:19 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6ce2cfe25..7d4f7586d 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -129,7 +129,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Exclude visibilities" ), Operation.parameter(:max_id, :query, :string, "Max ID"), - Operation.parameter(:min_id, :query, :string, "Mix ID"), + Operation.parameter(:min_id, :query, :string, "Return the oldest statuses newer than this id. "), Operation.parameter(:since_id, :query, :string, "Since ID"), Operation.parameter( :limit, -- cgit v1.2.3 From a45bd91d4e79ed354ab3903b195cf74e4327d4d0 Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:48:32 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 7d4f7586d..31dfbb098 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -116,7 +116,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)", parameters: [ %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, - Operation.parameter(:pinned, :query, BooleanLike, "Pinned"), + Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"), Operation.parameter(:tagged, :query, :string, "With tag"), Operation.parameter(:only_media, :query, BooleanLike, "Include only statuses with media attached"), Operation.parameter(:with_muted, :query, BooleanLike, "Include statuses from muted acccounts."), -- cgit v1.2.3 From 81a4c15816bf4fbe3e70ba1d34adff5dfaee1cbc Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 10:48:52 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 31dfbb098..dee28d1aa 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -128,7 +128,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do %Schema{type: :array, items: VisibilityScope}, "Exclude visibilities" ), - Operation.parameter(:max_id, :query, :string, "Max ID"), + Operation.parameter(:max_id, :query, :string, "Return statuses older than this id"), Operation.parameter(:min_id, :query, :string, "Return the oldest statuses newer than this id. "), Operation.parameter(:since_id, :query, :string, "Since ID"), Operation.parameter( -- cgit v1.2.3 From 5a2e45a2189514662f46a293f764682daba7b52d Mon Sep 17 00:00:00 2001 From: minibikini Date: Wed, 15 Apr 2020 11:29:10 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/account_operation.ex --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index dee28d1aa..92622e2ff 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -130,7 +130,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do ), Operation.parameter(:max_id, :query, :string, "Return statuses older than this id"), Operation.parameter(:min_id, :query, :string, "Return the oldest statuses newer than this id. "), - Operation.parameter(:since_id, :query, :string, "Since ID"), + Operation.parameter(:since_id, :query, :string, "Return the newest statuses newer than this id. "), Operation.parameter( :limit, :query, -- cgit v1.2.3 From 8ed162b65538ee3cb5b125587fd65657b36ca143 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 15 Apr 2020 15:39:32 +0400 Subject: Fix formatting --- .../web/api_spec/operations/account_operation.ex | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 92622e2ff..6c9de51bb 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -118,19 +118,38 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"), Operation.parameter(:tagged, :query, :string, "With tag"), - Operation.parameter(:only_media, :query, BooleanLike, "Include only statuses with media attached"), - Operation.parameter(:with_muted, :query, BooleanLike, "Include statuses from muted acccounts."), + Operation.parameter( + :only_media, + :query, + BooleanLike, + "Include only statuses with media attached" + ), + Operation.parameter( + :with_muted, + :query, + BooleanLike, + "Include statuses from muted acccounts." + ), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), - Operation.parameter( :exclude_visibilities, :query, %Schema{type: :array, items: VisibilityScope}, "Exclude visibilities" ), - Operation.parameter(:max_id, :query, :string, "Return statuses older than this id"), - Operation.parameter(:min_id, :query, :string, "Return the oldest statuses newer than this id. "), - Operation.parameter(:since_id, :query, :string, "Return the newest statuses newer than this id. "), + Operation.parameter(:max_id, :query, :string, "Return statuses older than this ID"), + Operation.parameter( + :min_id, + :query, + :string, + "Return the oldest statuses newer than this ID" + ), + Operation.parameter( + :since_id, + :query, + :string, + "Return the newest statuses newer than this ID" + ), Operation.parameter( :limit, :query, -- cgit v1.2.3 From 0e647ff55aa3128f45cd9df79b8af06da57c009e Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 15 Apr 2020 16:45:45 +0400 Subject: Abstract pagination params in OpenAPI spec --- lib/pleroma/web/api_spec/helpers.ex | 22 +++++ .../web/api_spec/operations/account_operation.ex | 108 +++++++-------------- 2 files changed, 57 insertions(+), 73 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index 7348dcbee..ce40fb9e8 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -3,6 +3,9 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Helpers do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + def request_body(description, schema_ref, opts \\ []) do media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"] @@ -24,4 +27,23 @@ defmodule Pleroma.Web.ApiSpec.Helpers do required: opts[:required] || false } end + + def pagination_params do + [ + Operation.parameter(:max_id, :query, :string, "Return items older than this ID"), + Operation.parameter(:min_id, :query, :string, "Return the oldest items newer than this ID"), + Operation.parameter( + :since_id, + :query, + :string, + "Return the newest items newer than this ID" + ), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 20, maximum: 40}, + "Limit" + ) + ] + end end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6c9de51bb..fe44a917a 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -6,7 +6,6 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Reference alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Helpers alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse @@ -21,6 +20,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias Pleroma.Web.ApiSpec.Schemas.StatusesResponse alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + import Pleroma.Web.ApiSpec.Helpers + @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -35,7 +36,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.", operationId: "AccountController.create", - requestBody: Helpers.request_body("Parameters", AccountCreateRequest, required: true), + requestBody: request_body("Parameters", AccountCreateRequest, required: true), responses: %{ 200 => Operation.response("Account", "application/json", AccountCreateResponse) } @@ -62,8 +63,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Update the user's display and preferences.", operationId: "AccountController.update_credentials", security: [%{"oAuth" => ["write:accounts"]}], - requestBody: - Helpers.request_body("Parameters", AccountUpdateCredentialsRequest, required: true), + requestBody: request_body("Parameters", AccountUpdateCredentialsRequest, required: true), responses: %{ 200 => Operation.response("Account", "application/json", Account) } @@ -114,49 +114,31 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do operationId: "AccountController.statuses", description: "Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)", - parameters: [ - %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, - Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"), - Operation.parameter(:tagged, :query, :string, "With tag"), - Operation.parameter( - :only_media, - :query, - BooleanLike, - "Include only statuses with media attached" - ), - Operation.parameter( - :with_muted, - :query, - BooleanLike, - "Include statuses from muted acccounts." - ), - Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), - Operation.parameter( - :exclude_visibilities, - :query, - %Schema{type: :array, items: VisibilityScope}, - "Exclude visibilities" - ), - Operation.parameter(:max_id, :query, :string, "Return statuses older than this ID"), - Operation.parameter( - :min_id, - :query, - :string, - "Return the oldest statuses newer than this ID" - ), - Operation.parameter( - :since_id, - :query, - :string, - "Return the newest statuses newer than this ID" - ), - Operation.parameter( - :limit, - :query, - %Schema{type: :integer, default: 20, maximum: 40}, - "Limit" - ) - ], + parameters: + [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"), + Operation.parameter(:tagged, :query, :string, "With tag"), + Operation.parameter( + :only_media, + :query, + BooleanLike, + "Include only statuses with media attached" + ), + Operation.parameter( + :with_muted, + :query, + BooleanLike, + "Include statuses from muted acccounts." + ), + Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), + Operation.parameter( + :exclude_visibilities, + :query, + %Schema{type: :array, items: VisibilityScope}, + "Exclude visibilities" + ) + ] ++ pagination_params(), responses: %{ 200 => Operation.response("Statuses", "application/json", StatusesResponse) } @@ -171,18 +153,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do security: [%{"oAuth" => ["read:accounts"]}], description: "Accounts which follow the given account, if network is not hidden by the account owner.", - parameters: [ - %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, - Operation.parameter(:max_id, :query, :string, "Max ID"), - Operation.parameter(:min_id, :query, :string, "Mix ID"), - Operation.parameter(:since_id, :query, :string, "Since ID"), - Operation.parameter( - :limit, - :query, - %Schema{type: :integer, default: 20, maximum: 40}, - "Limit" - ) - ], + parameters: + [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), responses: %{ 200 => Operation.response("Accounts", "application/json", AccountsResponse) } @@ -197,18 +169,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do security: [%{"oAuth" => ["read:accounts"]}], description: "Accounts which the given account is following, if network is not hidden by the account owner.", - parameters: [ - %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, - Operation.parameter(:max_id, :query, :string, "Max ID"), - Operation.parameter(:min_id, :query, :string, "Mix ID"), - Operation.parameter(:since_id, :query, :string, "Since ID"), - Operation.parameter( - :limit, - :query, - %Schema{type: :integer, default: 20, maximum: 40}, - "Limit" - ) - ], + parameters: + [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), responses: %{200 => Operation.response("Accounts", "application/json", AccountsResponse)} } end @@ -267,7 +229,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do summary: "Mute", operationId: "AccountController.mute", security: [%{"oAuth" => ["follow", "write:mutes"]}], - requestBody: Helpers.request_body("Parameters", AccountMuteRequest), + requestBody: request_body("Parameters", AccountMuteRequest), description: "Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).", parameters: [ @@ -334,7 +296,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do summary: "Follows", operationId: "AccountController.follows", security: [%{"oAuth" => ["follow", "write:follows"]}], - requestBody: Helpers.request_body("Parameters", AccountFollowsRequest, required: true), + requestBody: request_body("Parameters", AccountFollowsRequest, required: true), responses: %{ 200 => Operation.response("Account", "application/json", Account) } -- cgit v1.2.3 From 16f4787bf7e4849192d999eb2177ca7e1a34fbc9 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 15 Apr 2020 16:51:37 +0400 Subject: Add a TODO note --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1909ce097..5926a6cad 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1157,6 +1157,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + # TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only, + # the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2' and `restrict_muted/2` defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids}) when pinned in [true, "true", "1"] do from(activity in query, where: activity.id in ^ids) -- cgit v1.2.3 From 65f04b7806e342ed8967e5fa760e1509a776036e Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 15 Apr 2020 17:16:32 +0400 Subject: Fix credo warning --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5926a6cad..fa913a2aa 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1158,7 +1158,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end # TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only, - # the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2' and `restrict_muted/2` + # the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2' + # and `restrict_muted/2` + defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids}) when pinned in [true, "true", "1"] do from(activity in query, where: activity.id in ^ids) -- cgit v1.2.3 From bde1189c349dc114aca2e9310dda840a1007825f Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 15 Apr 2020 21:19:16 +0300 Subject: [#2349] Made :skip_plug/2 prevent plug from being executed even if explicitly called. Refactoring. Tests. --- lib/pleroma/plugs/auth_expected_plug.ex | 4 ++++ lib/pleroma/plugs/oauth_scopes_plug.ex | 6 +++--- lib/pleroma/tests/oauth_test_controller.ex | 31 +++++++++++++++++++++++++++++ lib/pleroma/web/router.ex | 11 ++++++++++ lib/pleroma/web/web.ex | 32 +++++++++++++++++++++++++++--- 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/tests/oauth_test_controller.ex (limited to 'lib') diff --git a/lib/pleroma/plugs/auth_expected_plug.ex b/lib/pleroma/plugs/auth_expected_plug.ex index 9e4a4bec8..f79597dc3 100644 --- a/lib/pleroma/plugs/auth_expected_plug.ex +++ b/lib/pleroma/plugs/auth_expected_plug.ex @@ -10,4 +10,8 @@ defmodule Pleroma.Plugs.AuthExpectedPlug do def call(conn, _) do put_private(conn, :auth_expected, true) end + + def auth_expected?(conn) do + conn.private[:auth_expected] + end end diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index b09e1bb4d..66f48c28c 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -10,13 +10,13 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.PlugHelper + use Pleroma.Web, :plug + @behaviour Plug def init(%{scopes: _} = options), do: options - def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do - conn = PlugHelper.append_to_called_plugs(conn, __MODULE__) - + def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] diff --git a/lib/pleroma/tests/oauth_test_controller.ex b/lib/pleroma/tests/oauth_test_controller.ex new file mode 100644 index 000000000..58d517f78 --- /dev/null +++ b/lib/pleroma/tests/oauth_test_controller.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +# A test controller reachable only in :test env. +# Serves to test OAuth scopes check skipping / enforcement. +defmodule Pleroma.Tests.OAuthTestController do + @moduledoc false + + use Pleroma.Web, :controller + + alias Pleroma.Plugs.OAuthScopesPlug + + plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth) + + plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth) + + def skipped_oauth(conn, _params) do + noop(conn) + end + + def performed_oauth(conn, _params) do + noop(conn) + end + + def missed_oauth(conn, _params) do + noop(conn) + end + + defp noop(conn), do: json(conn, %{}) +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 8d13cd6c9..c85ad9f8b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -672,6 +672,17 @@ defmodule Pleroma.Web.Router do end end + # Test-only routes needed to test action dispatching and plug chain execution + if Pleroma.Config.get(:env) == :test do + scope "/test/authenticated_api", Pleroma.Tests do + pipe_through(:authenticated_api) + + for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do + get("/#{action}", OAuthTestController, action) + end + end + end + scope "/", Pleroma.Web.MongooseIM do get("/user_exists", MongooseIMController, :user_exists) get("/check_password", MongooseIMController, :check_password) diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index 1af29ce78..ae7c94640 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -37,15 +37,21 @@ defmodule Pleroma.Web do put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) end - # Marks a plug as intentionally skipped - # (states that the plug is not called for a good reason, not by a mistake) + # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain defp skip_plug(conn, plug_module) do + try do + plug_module.ensure_skippable() + rescue + UndefinedFunctionError -> + raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." + end + PlugHelper.append_to_skipped_plugs(conn, plug_module) end # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed) defp action(conn, params) do - if conn.private[:auth_expected] && + if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do conn |> render_error( @@ -119,6 +125,26 @@ defmodule Pleroma.Web do end end + def plug do + quote do + alias Pleroma.Plugs.PlugHelper + + def ensure_skippable, do: :noop + + @impl Plug + @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." + def call(%Plug.Conn{} = conn, options) do + if PlugHelper.plug_skipped?(conn, __MODULE__) do + conn + else + conn + |> PlugHelper.append_to_called_plugs(__MODULE__) + |> perform(options) + end + end + end + end + @doc """ When used, dispatch to the appropriate controller/view/etc. """ -- cgit v1.2.3 From cf4ebba77471f188ce7da45df0b9ea76dbe31916 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 15 Apr 2020 22:59:25 +0400 Subject: Cleanup SubscriptionController --- .../controllers/subscription_controller.ex | 34 ++++++++++++---------- .../mastodon_api/views/push_subscription_view.ex | 19 ------------ .../web/mastodon_api/views/subscription_view.ex | 19 ++++++++++++ 3 files changed, 37 insertions(+), 35 deletions(-) delete mode 100644 lib/pleroma/web/mastodon_api/views/push_subscription_view.ex create mode 100644 lib/pleroma/web/mastodon_api/views/subscription_view.ex (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 11df6fc4a..4647c1f96 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -6,25 +6,22 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do @moduledoc "The module represents functions to manage user subscriptions." use Pleroma.Web, :controller - alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View alias Pleroma.Web.Push alias Pleroma.Web.Push.Subscription action_fallback(:errors) plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(:restrict_push_enabled) # Creates PushSubscription # POST /api/v1/push/subscription # def create(%{assigns: %{user: user, token: token}} = conn, params) do - with true <- Push.enabled(), - {:ok, _} <- Subscription.delete_if_exists(user, token), + with {:ok, _} <- Subscription.delete_if_exists(user, token), {:ok, subscription} <- Subscription.create(user, token, params) do - view = View.render("push_subscription.json", subscription: subscription) - json(conn, view) + render(conn, "show.json", subscription: subscription) end end @@ -32,10 +29,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do # GET /api/v1/push/subscription # def get(%{assigns: %{user: user, token: token}} = conn, _params) do - with true <- Push.enabled(), - {:ok, subscription} <- Subscription.get(user, token) do - view = View.render("push_subscription.json", subscription: subscription) - json(conn, view) + with {:ok, subscription} <- Subscription.get(user, token) do + render(conn, "show.json", subscription: subscription) end end @@ -43,10 +38,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do # PUT /api/v1/push/subscription # def update(%{assigns: %{user: user, token: token}} = conn, params) do - with true <- Push.enabled(), - {:ok, subscription} <- Subscription.update(user, token, params) do - view = View.render("push_subscription.json", subscription: subscription) - json(conn, view) + with {:ok, subscription} <- Subscription.update(user, token, params) do + render(conn, "show.json", subscription: subscription) end end @@ -54,11 +47,20 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do # DELETE /api/v1/push/subscription # def delete(%{assigns: %{user: user, token: token}} = conn, _params) do - with true <- Push.enabled(), - {:ok, _response} <- Subscription.delete(user, token), + with {:ok, _response} <- Subscription.delete(user, token), do: json(conn, %{}) end + defp restrict_push_enabled(conn, _) do + if Push.enabled() do + conn + else + conn + |> render_error(:forbidden, "Web push subscription is disabled on this Pleroma instance") + |> halt() + end + end + # fallback action # def errors(conn, {:error, :not_found}) do diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex deleted file mode 100644 index d32cef6e2..000000000 --- a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex +++ /dev/null @@ -1,19 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do - use Pleroma.Web, :view - alias Pleroma.Web.Push - - def render("push_subscription.json", %{subscription: subscription}) do - %{ - id: to_string(subscription.id), - endpoint: subscription.endpoint, - alerts: Map.get(subscription.data, "alerts"), - server_key: server_key() - } - end - - defp server_key, do: Keyword.get(Push.vapid_config(), :public_key) -end diff --git a/lib/pleroma/web/mastodon_api/views/subscription_view.ex b/lib/pleroma/web/mastodon_api/views/subscription_view.ex new file mode 100644 index 000000000..7c67cc924 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/subscription_view.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SubscriptionView do + use Pleroma.Web, :view + alias Pleroma.Web.Push + + def render("show.json", %{subscription: subscription}) do + %{ + id: to_string(subscription.id), + endpoint: subscription.endpoint, + alerts: Map.get(subscription.data, "alerts"), + server_key: server_key() + } + end + + defp server_key, do: Keyword.get(Push.vapid_config(), :public_key) +end -- cgit v1.2.3 From 72ef6cc4f2f601e26ba84c16ad2c91bd72867629 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 13 Apr 2020 14:07:23 +0300 Subject: added need_reboot endpoint to admin api --- lib/pleroma/web/admin_api/admin_api_controller.ex | 35 ++++++++--------------- lib/pleroma/web/router.ex | 1 + 2 files changed, 13 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 831c3bd02..8de7d70a3 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -914,16 +914,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end) |> List.flatten() - response = %{configs: merged} - - response = - if Restarter.Pleroma.need_reboot?() do - Map.put(response, :need_reboot, true) - else - response - end - - json(conn, response) + json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()}) end end @@ -950,28 +941,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do Config.TransferTask.load_and_update_env(deleted, false) - need_reboot? = - Restarter.Pleroma.need_reboot?() || - Enum.any?(updated, fn config -> + if !Restarter.Pleroma.need_reboot?() do + changed_reboot_settings? = + (updated ++ deleted) + |> Enum.any?(fn config -> group = ConfigDB.from_string(config.group) key = ConfigDB.from_string(config.key) value = ConfigDB.from_binary(config.value) Config.TransferTask.pleroma_need_restart?(group, key, value) end) - response = %{configs: updated} - - response = - if need_reboot? do - Restarter.Pleroma.need_reboot() - Map.put(response, :need_reboot, need_reboot?) - else - response - end + if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot() + end conn |> put_view(ConfigView) - |> render("index.json", response) + |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()}) end end @@ -983,6 +968,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def need_reboot(conn, _params) do + json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()}) + end + defp configurable_from_database(conn) do if Config.get(:configurable_from_database) do :ok diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 5f5ec1c81..fd94913a1 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -203,6 +203,7 @@ defmodule Pleroma.Web.Router do get("/config", AdminAPIController, :config_show) post("/config", AdminAPIController, :config_update) get("/config/descriptions", AdminAPIController, :config_descriptions) + get("/need_reboot", AdminAPIController, :need_reboot) get("/restart", AdminAPIController, :restart) get("/moderation_log", AdminAPIController, :list_log) -- cgit v1.2.3 From 77ee64b9930bf6b439f87112fa35e302f5125aa2 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 16 Apr 2020 17:54:57 +0300 Subject: user: remove blank? --- lib/pleroma/user.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index fab405233..753b0c686 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -343,9 +343,15 @@ defmodule Pleroma.User do bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) + name = + case params[:name] do + name when is_binary(name) and byte_size(name) > 0 -> name + _ -> params[:nickname] + end + params = params - |> Map.put(:name, blank?(params[:name]) || params[:nickname]) + |> Map.put(:name, name) |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now()) |> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:bio, bio_limit) @@ -1599,9 +1605,6 @@ defmodule Pleroma.User do end end - defp blank?(""), do: nil - defp blank?(n), do: n - def ap_enabled?(%User{local: true}), do: true def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled def ap_enabled?(_), do: false -- cgit v1.2.3 From 4d330d9df13b7ff5d24fdd8b4eec1e111fa51297 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 16 Apr 2020 18:05:36 +0300 Subject: fix for use of published from different entities --- lib/pleroma/web/feed/feed_view.ex | 9 +++------ lib/pleroma/web/templates/feed/feed/_activity.atom.eex | 8 ++++---- lib/pleroma/web/templates/feed/feed/_activity.rss.eex | 8 ++++---- .../web/templates/feed/feed/_tag_activity.atom.eex | 16 ++++++++-------- .../web/templates/feed/feed/_tag_activity.xml.eex | 15 +++++++-------- 5 files changed, 26 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index e18adaea8..1ae03e7e2 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Web.Feed.FeedView do def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") def prepare_activity(activity, opts \\ []) do - object = activity_object(activity) + object = Object.normalize(activity) actor = if opts[:actor] do @@ -33,7 +33,6 @@ defmodule Pleroma.Web.Feed.FeedView do %{ activity: activity, data: Map.get(object, :data), - object: object, actor: actor } end @@ -68,9 +67,7 @@ defmodule Pleroma.Web.Feed.FeedView do def last_activity(activities), do: List.last(activities) - def activity_object(activity), do: Object.normalize(activity) - - def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do + def activity_title(%{"content" => content}, opts \\ %{}) do content |> Pleroma.Web.Metadata.Utils.scrub_html() |> Pleroma.Emoji.Formatter.demojify() @@ -78,7 +75,7 @@ defmodule Pleroma.Web.Feed.FeedView do |> escape() end - def activity_content(%{data: %{"content" => content}}) do + def activity_content(%{"content" => content}) do content |> String.replace(~r/[\n\r]/, "") |> escape() diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index ac8a75009..78350f2aa 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -2,10 +2,10 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post <%= @data["id"] %> - <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> - <%= activity_content(@object) %> - <%= @data["published"] %> - <%= @data["published"] %> + <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> + <%= activity_content(@data) %> + <%= @activity.data["published"] %> + <%= @activity.data["published"] %> <%= activity_context(@activity) %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index a4dbed638..a304a16af 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -2,10 +2,10 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post <%= @data["id"] %> - <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> - <%= activity_content(@object) %> - <%= @data["published"] %> - <%= @data["published"] %> + <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> + <%= activity_content(@data) %> + <%= @activity.data["published"] %> + <%= @activity.data["published"] %> <%= activity_context(@activity) %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index da4fa6d6c..cf5874a91 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -1,12 +1,12 @@ http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post - + <%= render @view_module, "_tag_author.atom", assigns %> - + <%= @data["id"] %> - <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> - <%= activity_content(@object) %> + <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> + <%= activity_content(@data) %> <%= if @activity.local do %> @@ -15,8 +15,8 @@ <% end %> - <%= @data["published"] %> - <%= @data["published"] %> + <%= @activity.data["published"] %> + <%= @activity.data["published"] %> <%= activity_context(@activity) %> @@ -26,7 +26,7 @@ <%= if @data["summary"] do %> <%= @data["summary"] %> <% end %> - + <%= for id <- @activity.recipients do %> <%= if id == Pleroma.Constants.as_public() do %> <% end %> <% end %> - + <%= for tag <- @data["tag"] || [] do %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex index 295574df1..2334e24a2 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -1,15 +1,14 @@ - <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> - - + <%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %> + + <%= activity_context(@activity) %> <%= activity_context(@activity) %> - <%= pub_date(@data["published"]) %> - - <%= activity_content(@object) %> + <%= pub_date(@activity.data["published"]) %> + + <%= activity_content(@data) %> <%= for attachment <- @data["attachment"] || [] do %> <% end %> - - + -- cgit v1.2.3 From 304ea09f4c9902a1f96f30541e6c5d253527dd47 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Fri, 17 Apr 2020 08:42:48 +0300 Subject: fix for logger configuration --- lib/pleroma/config/transfer_task.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 3871e1cbb..f4722f99d 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -122,7 +122,7 @@ defmodule Pleroma.Config.TransferTask do :ok = update_env(:logger, :backends, merged) end - defp configure({group, key, _, merged}) do + defp configure({_, key, _, merged}) when key in [:console, :ex_syslogger] do merged = if key == :console do put_in(merged[:format], merged[:format] <> "\n") @@ -136,7 +136,12 @@ defmodule Pleroma.Config.TransferTask do else: key Logger.configure_backend(backend, merged) - :ok = update_env(:logger, group, merged) + :ok = update_env(:logger, key, merged) + end + + defp configure({_, key, _, merged}) do + Logger.configure([{key, merged}]) + :ok = update_env(:logger, key, merged) end defp update({group, key, value, merged}) do -- cgit v1.2.3 From 6cda360fea8a42168b5835ef903cf3bf89c8151a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 16 Apr 2020 10:36:37 +0300 Subject: don't restart postgrex --- lib/pleroma/config/loader.ex | 2 +- lib/pleroma/config/transfer_task.ex | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 6ca6550bd..0f3ecf1ed 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -47,7 +47,7 @@ defmodule Pleroma.Config.Loader do @spec filter_group(atom(), keyword()) :: keyword() def filter_group(group, configs) do Enum.reject(configs[group], fn {key, _v} -> - key in @reject_keys or (group == :phoenix and key == :serve_endpoints) + key in @reject_keys or (group == :phoenix and key == :serve_endpoints) or group == :postgrex end) end end diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index f4722f99d..c02b70e96 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -46,14 +46,6 @@ defmodule Pleroma.Config.TransferTask do with {_, true} <- {:configurable, Config.get(:configurable_from_database)} do # We need to restart applications for loaded settings take effect - # TODO: some problem with prometheus after restart! - reject_restart = - if restart_pleroma? do - [nil, :prometheus] - else - [:pleroma, nil, :prometheus] - end - {logger, other} = (Repo.all(ConfigDB) ++ deleted_settings) |> Enum.map(&transform_and_merge/1) @@ -65,10 +57,20 @@ defmodule Pleroma.Config.TransferTask do started_applications = Application.started_applications() + # TODO: some problem with prometheus after restart! + reject = [nil, :prometheus, :postgrex] + + reject = + if restart_pleroma? do + reject + else + [:pleroma | reject] + end + other |> Enum.map(&update/1) |> Enum.uniq() - |> Enum.reject(&(&1 in reject_restart)) + |> Enum.reject(&(&1 in reject)) |> maybe_set_pleroma_last() |> Enum.each(&restart(started_applications, &1, Config.get(:env))) -- cgit v1.2.3 From 46f051048fb1afb02fe81b872ae9f595f2c5f2c1 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 17 Apr 2020 14:32:15 +0200 Subject: migrations/20200406100225_users_add_emoji: Fix tag to Emoji filtering --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 35af0f7dc..d403405a0 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1430,7 +1430,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do emojis = data |> Map.get("tag", []) - |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) + |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end) |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc -> Map.put(acc, String.trim(name, ":"), url) end) -- cgit v1.2.3 From 26d9c83316fe5d8a3bf1f8fadae727788a92a725 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 17 Apr 2020 15:50:15 +0200 Subject: SideEffects: Test for notification creation. --- lib/pleroma/web/activity_pub/side_effects.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 666a4e310..6a8f1af96 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -17,7 +17,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do def handle(%{data: %{"type" => "Like"}} = object, meta) do liked_object = Object.get_by_ap_id(object.data["object"]) Utils.add_like_to_object(object, liked_object) + Notification.create_notifications(object) + {:ok, object, meta} end -- cgit v1.2.3 From 163341857a726e8d74b6ddcd1230579e4c36a1b5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 17 Apr 2020 19:27:22 +0400 Subject: Improve OpenAPI errors --- lib/pleroma/web/api_spec/render_error.ex | 220 +++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index 9184c43b6..b5877ca9c 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -5,23 +5,227 @@ defmodule Pleroma.Web.ApiSpec.RenderError do @behaviour Plug - alias OpenApiSpex.Plug.JsonRenderError - alias Plug.Conn + import Plug.Conn, only: [put_status: 2] + import Phoenix.Controller, only: [json: 2] + import Pleroma.Web.Gettext @impl Plug def init(opts), do: opts @impl Plug - def call(%{private: %{open_api_spex: %{operation_id: "AccountController.create"}}} = conn, _) do + def call(conn, errors) do + errors = + Enum.map(errors, fn + %{name: nil} = err -> + %OpenApiSpex.Cast.Error{err | name: List.last(err.path)} + + err -> + err + end) + conn - |> Conn.put_status(:bad_request) - |> Phoenix.Controller.json(%{"error" => "Missing parameters"}) + |> put_status(:bad_request) + |> json(%{ + error: errors |> Enum.map(&message/1) |> Enum.join(" "), + errors: errors |> Enum.map(&render_error/1) + }) + end + + defp render_error(error) do + pointer = OpenApiSpex.path_to_string(error) + + %{ + title: "Invalid value", + source: %{ + pointer: pointer + }, + message: OpenApiSpex.Cast.Error.message(error) + } + end + + defp message(%{reason: :invalid_schema_type, type: type, name: name}) do + gettext("%{name} - Invalid schema.type. Got: %{type}.", + name: name, + type: inspect(type) + ) + end + + defp message(%{reason: :null_value, name: name} = error) do + case error.type do + nil -> + gettext("%{name} - null value.", name: name) + + type -> + gettext("%{name} - null value where %{type} expected.", + name: name, + type: type + ) + end + end + + defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do + gettext( + "Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.", + invalid_schema: invalid_schema + ) + end + + defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do + gettext("Failed to cast value using any of: %{failed_schemas}.", + failed_schemas: failed_schemas + ) + end + + defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do + gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas) end - def call(conn, reason) do - opts = JsonRenderError.init(reason) + defp message(%{reason: :min_length, length: length, name: name}) do + gettext("%{name} - String length is smaller than minLength: %{length}.", + name: name, + length: length + ) + end + + defp message(%{reason: :max_length, length: length, name: name}) do + gettext("%{name} - String length is larger than maxLength: %{length}.", + name: name, + length: length + ) + end + + defp message(%{reason: :unique_items, name: name}) do + gettext("%{name} - Array items must be unique.", name: name) + end + + defp message(%{reason: :min_items, length: min, value: array, name: name}) do + gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.", + name: name, + length: length(array), + min: min + ) + end + + defp message(%{reason: :max_items, length: max, value: array, name: name}) do + gettext("%{name} - Array length %{length} is larger than maxItems: %{}.", + name: name, + length: length(array), + max: max + ) + end + + defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do + gettext("%{name} - %{count} is not a multiple of %{multiple}.", + name: name, + count: count, + multiple: multiple + ) + end + + defp message(%{reason: :exclusive_max, length: max, value: value, name: name}) + when value >= max do + gettext("%{name} - %{value} is larger than exclusive maximum %{max}.", + name: name, + value: value, + max: max + ) + end + + defp message(%{reason: :maximum, length: max, value: value, name: name}) + when value > max do + gettext("%{name} - %{value} is larger than inclusive maximum %{max}.", + name: name, + value: value, + max: max + ) + end + + defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name}) + when value <= min do + gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.", + name: name, + value: value, + min: min + ) + end + + defp message(%{reason: :minimum, length: min, value: value, name: name}) + when value < min do + gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.", + name: name, + value: value, + min: min + ) + end + + defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do + gettext("%{name} - Invalid %{type}. Got: %{value}.", + name: name, + value: OpenApiSpex.TermType.type(value), + type: type + ) + end + + defp message(%{reason: :invalid_format, format: format, name: name}) do + gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format)) + end + + defp message(%{reason: :invalid_enum, name: name}) do + gettext("%{name} - Invalid value for enum.", name: name) + end + + defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do + gettext("Failed to cast to any schema in %{polymorphic_type}", + polymorphic_type: polymorphic_type + ) + end + + defp message(%{reason: :unexpected_field, name: name}) do + gettext("Unexpected field: %{name}.", name: safe_string(name)) + end + + defp message(%{reason: :no_value_for_discriminator, name: field}) do + gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field) + end + + defp message(%{reason: :invalid_discriminator_value, name: field}) do + gettext("No value provided for required discriminator `%{field}`.", name: field) + end + + defp message(%{reason: :unknown_schema, name: name}) do + gettext("Unknown schema: %{name}.", name: name) + end + + defp message(%{reason: :missing_field, name: name}) do + gettext("Missing field: %{name}.", name: name) + end + + defp message(%{reason: :missing_header, name: name}) do + gettext("Missing header: %{name}.", name: name) + end + + defp message(%{reason: :invalid_header, name: name}) do + gettext("Invalid value for header: %{name}.", name: name) + end + + defp message(%{reason: :max_properties, meta: meta}) do + gettext( + "Object property count %{property_count} is greater than maxProperties: %{max_properties}.", + property_count: meta.property_count, + max_properties: meta.max_properties + ) + end + + defp message(%{reason: :min_properties, meta: meta}) do + gettext( + "Object property count %{property_count} is less than minProperties: %{min_properties}", + property_count: meta.property_count, + min_properties: meta.min_properties + ) + end - JsonRenderError.call(conn, opts) + defp safe_string(string) do + to_string(string) |> String.slice(0..39) end end -- cgit v1.2.3 From 66f55106bda23e0cfb01cb63f7397f4383518963 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 17 Apr 2020 21:21:10 +0300 Subject: [#1682] Fixed Basic Auth permissions issue by disabling OAuth scopes checks when password is provided. Refactored plugs skipping functionality. --- lib/pleroma/plugs/authentication_plug.ex | 6 +++++- lib/pleroma/plugs/legacy_authentication_plug.ex | 3 +++ lib/pleroma/plugs/plug_helper.ex | 24 +++++++++++---------- lib/pleroma/web/web.ex | 28 ++++++++++++++++++------- 4 files changed, 42 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 089028d77..0061c69dc 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -4,8 +4,11 @@ defmodule Pleroma.Plugs.AuthenticationPlug do alias Comeonin.Pbkdf2 - import Plug.Conn + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + + import Plug.Conn + require Logger def init(options), do: options @@ -37,6 +40,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do if Pbkdf2.checkpw(password, password_hash) do conn |> assign(:user, auth_user) + |> OAuthScopesPlug.skip_plug() else conn end diff --git a/lib/pleroma/plugs/legacy_authentication_plug.ex b/lib/pleroma/plugs/legacy_authentication_plug.ex index 5c5c36c56..d346e01a6 100644 --- a/lib/pleroma/plugs/legacy_authentication_plug.ex +++ b/lib/pleroma/plugs/legacy_authentication_plug.ex @@ -4,6 +4,8 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlug do import Plug.Conn + + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User def init(options) do @@ -27,6 +29,7 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlug do conn |> assign(:auth_user, user) |> assign(:user, user) + |> OAuthScopesPlug.skip_plug() else _ -> conn diff --git a/lib/pleroma/plugs/plug_helper.ex b/lib/pleroma/plugs/plug_helper.ex index 4f83e9414..9c67be8ef 100644 --- a/lib/pleroma/plugs/plug_helper.ex +++ b/lib/pleroma/plugs/plug_helper.ex @@ -5,30 +5,32 @@ defmodule Pleroma.Plugs.PlugHelper do @moduledoc "Pleroma Plug helper" - def append_to_called_plugs(conn, plug_module) do - append_to_private_list(conn, :called_plugs, plug_module) - end + @called_plugs_list_id :called_plugs + def called_plugs_list_id, do: @called_plugs_list_id - def append_to_skipped_plugs(conn, plug_module) do - append_to_private_list(conn, :skipped_plugs, plug_module) - end + @skipped_plugs_list_id :skipped_plugs + def skipped_plugs_list_id, do: @skipped_plugs_list_id + @doc "Returns `true` if specified plug was called." def plug_called?(conn, plug_module) do - contained_in_private_list?(conn, :called_plugs, plug_module) + contained_in_private_list?(conn, @called_plugs_list_id, plug_module) end + @doc "Returns `true` if specified plug was explicitly marked as skipped." def plug_skipped?(conn, plug_module) do - contained_in_private_list?(conn, :skipped_plugs, plug_module) + contained_in_private_list?(conn, @skipped_plugs_list_id, plug_module) end + @doc "Returns `true` if specified plug was either called or explicitly marked as skipped." def plug_called_or_skipped?(conn, plug_module) do plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module) end - defp append_to_private_list(conn, private_variable, value) do - list = conn.private[private_variable] || [] + # Appends plug to known list (skipped, called). Intended to be used from within plug code only. + def append_to_private_list(conn, list_id, value) do + list = conn.private[list_id] || [] modified_list = Enum.uniq(list ++ [value]) - Plug.Conn.put_private(conn, private_variable, modified_list) + Plug.Conn.put_private(conn, list_id, modified_list) end defp contained_in_private_list?(conn, private_variable, value) do diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index ae7c94640..bf48ce26c 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -40,17 +40,22 @@ defmodule Pleroma.Web do # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain defp skip_plug(conn, plug_module) do try do - plug_module.ensure_skippable() + plug_module.skip_plug(conn) rescue UndefinedFunctionError -> raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." end - - PlugHelper.append_to_skipped_plugs(conn, plug_module) end - # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed) + # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do + with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do + super(conn, params) + end + end + + # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check + defp maybe_halt_on_missing_oauth_scopes_check(conn) do if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do conn @@ -60,7 +65,7 @@ defmodule Pleroma.Web do ) |> halt() else - super(conn, params) + conn end end end @@ -129,7 +134,16 @@ defmodule Pleroma.Web do quote do alias Pleroma.Plugs.PlugHelper - def ensure_skippable, do: :noop + @doc """ + Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain. + """ + def skip_plug(conn) do + PlugHelper.append_to_private_list( + conn, + PlugHelper.skipped_plugs_list_id(), + __MODULE__ + ) + end @impl Plug @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." @@ -138,7 +152,7 @@ defmodule Pleroma.Web do conn else conn - |> PlugHelper.append_to_called_plugs(__MODULE__) + |> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__) |> perform(options) end end -- cgit v1.2.3 From eb61564005b743acefe7bb31c9369c38c9dfad6e Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 17 Apr 2020 23:55:56 +0200 Subject: migrations/20200406100225_users_add_emoji: Fix tag to Emoji filtering, electric bongaloo --- lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d403405a0..cb942c211 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1430,7 +1430,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do emojis = data |> Map.get("tag", []) - |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end) + |> Enum.filter(fn + %{"type" => t} -> t == "Emoji" + _ -> false + end) |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc -> Map.put(acc, String.trim(name, ":"), url) end) -- cgit v1.2.3 From 24f760c2f732465151655fd4cd69cc149546b29f Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 17 Apr 2020 22:48:37 +0000 Subject: Apply suggestion to lib/pleroma/web/activity_pub/activity_pub.ex --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index cb942c211..eedea08a2 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1431,7 +1431,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do data |> Map.get("tag", []) |> Enum.filter(fn - %{"type" => t} -> t == "Emoji" + %{"type" => "Emoji"} -> true _ -> false end) |> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc -> -- cgit v1.2.3 From 258d8975797298b9eadddb48a8a2fcf3a9dbf211 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 20 Apr 2020 16:38:00 +0400 Subject: Cleanup and DRY the Router --- lib/pleroma/web/router.ex | 113 ++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 73 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 7e5960949..153802a43 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -16,79 +16,60 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.UserEnabledPlug) end - pipeline :api do - plug(:accepts, ["json"]) - plug(:fetch_session) + pipeline :authenticate do plug(Pleroma.Plugs.OAuthPlug) plug(Pleroma.Plugs.BasicAuthDecoderPlug) plug(Pleroma.Plugs.UserFetcherPlug) plug(Pleroma.Plugs.SessionAuthenticationPlug) plug(Pleroma.Plugs.LegacyAuthenticationPlug) plug(Pleroma.Plugs.AuthenticationPlug) + end + + pipeline :after_auth do plug(Pleroma.Plugs.UserEnabledPlug) plug(Pleroma.Plugs.SetUserSessionIdPlug) plug(Pleroma.Plugs.EnsureUserKeyPlug) - plug(Pleroma.Plugs.IdempotencyPlug) - plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) end - pipeline :authenticated_api do + pipeline :base_api do plug(:accepts, ["json"]) plug(:fetch_session) + plug(:authenticate) + plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) + end + + pipeline :api do + plug(:base_api) + plug(:after_auth) + plug(Pleroma.Plugs.IdempotencyPlug) + end + + pipeline :authenticated_api do + plug(:base_api) plug(Pleroma.Plugs.AuthExpectedPlug) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.LegacyAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) - plug(Pleroma.Plugs.UserEnabledPlug) - plug(Pleroma.Plugs.SetUserSessionIdPlug) + plug(:after_auth) plug(Pleroma.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Plugs.IdempotencyPlug) - plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) end pipeline :admin_api do - plug(:accepts, ["json"]) - plug(:fetch_session) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.LegacyAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) + plug(:base_api) plug(Pleroma.Plugs.AdminSecretAuthenticationPlug) - plug(Pleroma.Plugs.UserEnabledPlug) - plug(Pleroma.Plugs.SetUserSessionIdPlug) + plug(:after_auth) plug(Pleroma.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Plugs.UserIsAdminPlug) plug(Pleroma.Plugs.IdempotencyPlug) - plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) end pipeline :mastodon_html do - plug(:accepts, ["html"]) - plug(:fetch_session) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.LegacyAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) - plug(Pleroma.Plugs.UserEnabledPlug) - plug(Pleroma.Plugs.SetUserSessionIdPlug) - plug(Pleroma.Plugs.EnsureUserKeyPlug) + plug(:browser) + plug(:authenticate) + plug(:after_auth) end pipeline :pleroma_html do - plug(:accepts, ["html"]) - plug(:fetch_session) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) + plug(:browser) + plug(:authenticate) plug(Pleroma.Plugs.EnsureUserKeyPlug) end @@ -515,7 +496,7 @@ defmodule Pleroma.Web.Router do end scope "/api" do - pipe_through(:api) + pipe_through(:base_api) get("/openapi", OpenApiSpex.Plug.RenderSpec, []) end @@ -529,10 +510,6 @@ defmodule Pleroma.Web.Router do post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) end - pipeline :ap_service_actor do - plug(:accepts, ["activity+json", "json"]) - end - pipeline :ostatus do plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"]) plug(Pleroma.Plugs.StaticFEPlug) @@ -543,8 +520,7 @@ defmodule Pleroma.Web.Router do end scope "/", Pleroma.Web do - pipe_through(:ostatus) - pipe_through(:http_signature) + pipe_through([:ostatus, :http_signature]) get("/objects/:uuid", OStatus.OStatusController, :object) get("/activities/:uuid", OStatus.OStatusController, :activity) @@ -562,13 +538,6 @@ defmodule Pleroma.Web.Router do get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe) end - # Server to Server (S2S) AP interactions - pipeline :activitypub do - plug(:accepts, ["activity+json", "json"]) - plug(Pleroma.Web.Plugs.HTTPSignaturePlug) - plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug) - end - scope "/", Pleroma.Web.ActivityPub do # XXX: not really ostatus pipe_through(:ostatus) @@ -576,19 +545,22 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/outbox", ActivityPubController, :outbox) end + pipeline :ap_service_actor do + plug(:accepts, ["activity+json", "json"]) + end + + # Server to Server (S2S) AP interactions + pipeline :activitypub do + plug(:ap_service_actor) + plug(:http_signature) + end + # Client to Server (C2S) AP interactions pipeline :activitypub_client do - plug(:accepts, ["activity+json", "json"]) + plug(:ap_service_actor) plug(:fetch_session) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.LegacyAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) - plug(Pleroma.Plugs.UserEnabledPlug) - plug(Pleroma.Plugs.SetUserSessionIdPlug) - plug(Pleroma.Plugs.EnsureUserKeyPlug) + plug(:authenticate) + plug(:after_auth) end scope "/", Pleroma.Web.ActivityPub do @@ -660,12 +632,7 @@ defmodule Pleroma.Web.Router do get("/web/*path", MastoFEController, :index) end - pipeline :remote_media do - end - scope "/proxy/", Pleroma.Web.MediaProxy do - pipe_through(:remote_media) - get("/:sig/:url", MediaProxyController, :remote) get("/:sig/:url/:filename", MediaProxyController, :remote) end -- cgit v1.2.3 From 8b4de61d6449f70e0a5e84be3082724c7f50ffee Mon Sep 17 00:00:00 2001 From: Ilja Date: Mon, 20 Apr 2020 12:59:16 +0000 Subject: Fix ObjectAgePolicyTest The policy didn't block old posts as it should. * I fixed it and tested on a test server * I added the settings to description so that this information is shown in nodeinfo * TODO: I didn't work TTD and still need to fix the tests --- lib/pleroma/web/activity_pub/mrf/object_age_policy.ex | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 4a8bc91ae..b0ccb63c8 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do @moduledoc "Filter activities depending on their age" @behaviour Pleroma.Web.ActivityPub.MRF - defp check_date(%{"published" => published} = message) do + defp check_date(%{"object" => %{"published" => published}} = message) do with %DateTime{} = now <- DateTime.utc_now(), {:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published), max_ttl <- Config.get([:mrf_object_age, :threshold]), @@ -96,5 +96,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do def filter(message), do: {:ok, message} @impl true - def describe, do: {:ok, %{}} + def describe do + mrf_object_age = + Pleroma.Config.get(:mrf_object_age) + |> Enum.into(%{}) + + {:ok, %{mrf_object_age: mrf_object_age}} + end end -- cgit v1.2.3 From ed3974af248a1b201d2008f1a128ee53550ef78b Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 20 Apr 2020 18:39:05 +0400 Subject: Add OpenAPI spec for `AccountController.identity_proofs` operation --- lib/pleroma/web/api_spec/operations/account_operation.ex | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index fe44a917a..d3cebaf05 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -341,4 +341,16 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } } end + + def identity_proofs_operation do + %Operation{ + tags: ["accounts"], + summary: "Identity proofs", + operationId: "AccountController.identity_proofs", + description: "Not implemented", + responses: %{ + 200 => Operation.response("Empry array", "application/json", %Schema{type: :array}) + } + } + end end -- cgit v1.2.3 From b54c8813d632cb44c7deb207e91bd32f01f33794 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 13 Apr 2020 13:48:32 -0500 Subject: Add :reject_deletes option to SimplePolicy --- lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index b23f263f5..b7dcb1b86 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -149,7 +149,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_banner_removal(_actor_info, object), do: {:ok, object} @impl true - def filter(%{"type" => "Delete"} = object), do: {:ok, object} + def filter(%{"type" => "Delete", "actor" => actor} = object) do + %{host: actor_host} = URI.parse(actor) + + reject_deletes = + Pleroma.Config.get([:mrf_simple, :reject_deletes]) + |> MRF.subdomains_regex() + + if MRF.subdomain_match?(reject_deletes, actor_host) do + {:reject, nil} + else + {:ok, object} + end + end @impl true def filter(%{"actor" => actor} = object) do -- cgit v1.2.3 From f685cbd30940b3fd92a2f6c8a161729bc2ceaab6 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 21 Apr 2020 16:29:19 +0300 Subject: Automatic checks of authentication / instance publicity. Definition of missing OAuth scopes in AdminAPIController. Refactoring. --- lib/pleroma/plugs/auth_expected_plug.ex | 17 --- lib/pleroma/plugs/ensure_authenticated_plug.ex | 8 +- .../plugs/ensure_public_or_authenticated_plug.ex | 6 +- .../plugs/expect_authenticated_check_plug.ex | 20 +++ .../expect_public_or_authenticated_check_plug.ex | 21 +++ lib/pleroma/plugs/oauth_scopes_plug.ex | 14 +- lib/pleroma/web/admin_api/admin_api_controller.ex | 36 +++-- lib/pleroma/web/fallback_redirect_controller.ex | 2 + lib/pleroma/web/masto_fe_controller.ex | 7 +- .../mastodon_api/controllers/account_controller.ex | 26 ++-- .../mastodon_api/controllers/auth_controller.ex | 4 +- .../controllers/conversation_controller.ex | 6 +- .../controllers/domain_block_controller.ex | 2 - .../mastodon_api/controllers/filter_controller.ex | 2 - .../controllers/follow_request_controller.ex | 2 - .../mastodon_api/controllers/list_controller.ex | 8 +- .../mastodon_api/controllers/marker_controller.ex | 2 +- .../controllers/mastodon_api_controller.ex | 2 - .../mastodon_api/controllers/media_controller.ex | 2 - .../controllers/notification_controller.ex | 2 - .../mastodon_api/controllers/poll_controller.ex | 2 - .../mastodon_api/controllers/report_controller.ex | 2 - .../controllers/scheduled_activity_controller.ex | 2 - .../mastodon_api/controllers/search_controller.ex | 2 - .../mastodon_api/controllers/status_controller.ex | 2 +- .../controllers/subscription_controller.ex | 2 +- .../controllers/timeline_controller.ex | 2 +- .../web/media_proxy/media_proxy_controller.ex | 1 + .../pleroma_api/controllers/account_controller.ex | 14 +- .../controllers/emoji_api_controller.ex | 16 ++- .../pleroma_api/controllers/mascot_controller.ex | 2 - .../controllers/pleroma_api_controller.ex | 16 ++- .../pleroma_api/controllers/scrobble_controller.ex | 2 - lib/pleroma/web/router.ex | 152 +++++++++++---------- .../web/twitter_api/controllers/util_controller.ex | 7 - .../web/twitter_api/twitter_api_controller.ex | 14 +- lib/pleroma/web/web.ex | 85 +++++++++--- 37 files changed, 302 insertions(+), 210 deletions(-) delete mode 100644 lib/pleroma/plugs/auth_expected_plug.ex create mode 100644 lib/pleroma/plugs/expect_authenticated_check_plug.ex create mode 100644 lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex (limited to 'lib') diff --git a/lib/pleroma/plugs/auth_expected_plug.ex b/lib/pleroma/plugs/auth_expected_plug.ex deleted file mode 100644 index f79597dc3..000000000 --- a/lib/pleroma/plugs/auth_expected_plug.ex +++ /dev/null @@ -1,17 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.AuthExpectedPlug do - import Plug.Conn - - def init(options), do: options - - def call(conn, _) do - put_private(conn, :auth_expected, true) - end - - def auth_expected?(conn) do - conn.private[:auth_expected] - end -end diff --git a/lib/pleroma/plugs/ensure_authenticated_plug.ex b/lib/pleroma/plugs/ensure_authenticated_plug.ex index 054d2297f..9c8f5597f 100644 --- a/lib/pleroma/plugs/ensure_authenticated_plug.ex +++ b/lib/pleroma/plugs/ensure_authenticated_plug.ex @@ -5,17 +5,21 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do import Plug.Conn import Pleroma.Web.TranslationHelpers + alias Pleroma.User + use Pleroma.Web, :plug + def init(options) do options end - def call(%{assigns: %{user: %User{}}} = conn, _) do + @impl true + def perform(%{assigns: %{user: %User{}}} = conn, _) do conn end - def call(conn, options) do + def perform(conn, options) do perform = cond do options[:if_func] -> options[:if_func].() diff --git a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex index d980ff13d..7265bb87a 100644 --- a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex +++ b/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex @@ -5,14 +5,18 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do import Pleroma.Web.TranslationHelpers import Plug.Conn + alias Pleroma.Config alias Pleroma.User + use Pleroma.Web, :plug + def init(options) do options end - def call(conn, _) do + @impl true + def perform(conn, _) do public? = Config.get!([:instance, :public]) case {public?, conn} do diff --git a/lib/pleroma/plugs/expect_authenticated_check_plug.ex b/lib/pleroma/plugs/expect_authenticated_check_plug.ex new file mode 100644 index 000000000..66b8d5de5 --- /dev/null +++ b/lib/pleroma/plugs/expect_authenticated_check_plug.ex @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.ExpectAuthenticatedCheckPlug do + @moduledoc """ + Marks `Pleroma.Plugs.EnsureAuthenticatedPlug` as expected to be executed later in plug chain. + + No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`). + """ + + use Pleroma.Web, :plug + + def init(options), do: options + + @impl true + def perform(conn, _) do + conn + end +end diff --git a/lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex b/lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex new file mode 100644 index 000000000..ba0ef76bd --- /dev/null +++ b/lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug do + @moduledoc """ + Marks `Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug` as expected to be executed later in plug + chain. + + No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`). + """ + + use Pleroma.Web, :plug + + def init(options), do: options + + @impl true + def perform(conn, _) do + conn + end +end diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 66f48c28c..a61582566 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -7,15 +7,12 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do import Pleroma.Web.Gettext alias Pleroma.Config - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.PlugHelper use Pleroma.Web, :plug - @behaviour Plug - def init(%{scopes: _} = options), do: options + @impl true def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] @@ -34,7 +31,6 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do conn |> assign(:user, nil) |> assign(:token, nil) - |> maybe_perform_instance_privacy_check(options) true -> missing_scopes = scopes -- matched_scopes @@ -71,12 +67,4 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do scopes end end - - defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do - if options[:skip_instance_privacy_check] do - conn - else - EnsurePublicOrAuthenticatedPlug.call(conn, []) - end - end end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 9c79310c0..816c11e01 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -48,6 +48,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do %{scopes: ["write:accounts"], admin: true} when action in [ :get_password_reset, + :force_password_reset, :user_delete, :users_create, :user_toggle_activation, @@ -56,7 +57,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do :tag_users, :untag_users, :right_add, + :right_add_multiple, :right_delete, + :right_delete_multiple, :update_user_credentials ] ) @@ -84,13 +87,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["write:reports"], admin: true} - when action in [:reports_update] + when action in [:reports_update, :report_notes_create, :report_notes_delete] ) plug( OAuthScopesPlug, %{scopes: ["read:statuses"], admin: true} - when action == :list_user_statuses + when action in [:list_statuses, :list_user_statuses, :list_instance_statuses] ) plug( @@ -102,13 +105,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["read"], admin: true} - when action in [:config_show, :list_log, :stats] + when action in [ + :config_show, + :list_log, + :stats, + :relay_list, + :config_descriptions, + :need_reboot + ] ) plug( OAuthScopesPlug, %{scopes: ["write"], admin: true} - when action == :config_update + when action in [ + :restart, + :config_update, + :resend_confirmation_email, + :confirm_email, + :oauth_app_create, + :oauth_app_list, + :oauth_app_update, + :oauth_app_delete, + :reload_emoji + ] ) action_fallback(:errors) @@ -1103,25 +1123,25 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> json(%{"status_visibility" => count}) end - def errors(conn, {:error, :not_found}) do + defp errors(conn, {:error, :not_found}) do conn |> put_status(:not_found) |> json(dgettext("errors", "Not found")) end - def errors(conn, {:error, reason}) do + defp errors(conn, {:error, reason}) do conn |> put_status(:bad_request) |> json(reason) end - def errors(conn, {:param_cast, _}) do + defp errors(conn, {:param_cast, _}) do conn |> put_status(:bad_request) |> json(dgettext("errors", "Invalid parameters")) end - def errors(conn, _) do + defp errors(conn, _) do conn |> put_status(:internal_server_error) |> json(dgettext("errors", "Something went wrong")) diff --git a/lib/pleroma/web/fallback_redirect_controller.ex b/lib/pleroma/web/fallback_redirect_controller.ex index c13518030..0d9d578fc 100644 --- a/lib/pleroma/web/fallback_redirect_controller.ex +++ b/lib/pleroma/web/fallback_redirect_controller.ex @@ -4,7 +4,9 @@ defmodule Fallback.RedirectController do use Pleroma.Web, :controller + require Logger + alias Pleroma.User alias Pleroma.Web.Metadata diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 557cde328..9a2ec517a 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -13,11 +13,14 @@ defmodule Pleroma.Web.MastoFEController do # Note: :index action handles attempt of unauthenticated access to private instance with redirect plug( OAuthScopesPlug, - %{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true} + %{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :index ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest]) + plug( + :skip_plug, + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :manifest] + ) @doc "GET /web/*path" def index(%{assigns: %{user: user, token: token}} = conn, _params) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index e8e59ac66..9b8cc0d0d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -26,12 +26,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI - plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs) + plug(:skip_plug, OAuthScopesPlug when action in [:create, :identity_proofs]) + + plug( + :skip_plug, + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + when action in [:create, :show, :statuses] + ) plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} - when action == :show + when action in [:show, :endorsements] + ) + + plug( + OAuthScopesPlug, + %{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]} + when action == :statuses ) plug( @@ -56,21 +68,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships) - # Note: :follows (POST /api/v1/follows) is the same as :follow, consider removing :follows plug( OAuthScopesPlug, - %{scopes: ["follow", "write:follows"]} when action in [:follows, :follow, :unfollow] + %{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow] ) plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) - plug( - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - when action not in [:create, :show, :statuses] - ) - @relationship_actions [:follow, :unfollow] @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a @@ -356,7 +362,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/follows" - def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do + def follow_by_uri(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, {_, true} <- {:followed, follower.id != followed.id}, {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex index 37b389382..753b3db3e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex @@ -13,10 +13,10 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - @local_mastodon_name "Mastodon-Local" - plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset) + @local_mastodon_name "Mastodon-Local" + @doc "GET /web/login" def login(%{assigns: %{user: %User{}}} = conn, _params) do redirect(conn, to: local_mastodon_root_path(conn)) diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex index 7c9b11bf1..c44641526 100644 --- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex @@ -14,9 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index) - plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :read) - - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action != :index) @doc "GET /api/v1/conversations" def index(%{assigns: %{user: user}} = conn, params) do @@ -28,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do end @doc "POST /api/v1/conversations/:id/read" - def read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do + def mark_as_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do with %Participation{} = participation <- Repo.get_by(Participation, id: participation_id, user_id: user.id), {:ok, participation} <- Participation.mark_as_read(participation) do diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index 84de79413..c4fa383f2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do %{scopes: ["follow", "write:blocks"]} when action != :index ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "GET /api/v1/domain_blocks" def index(%{assigns: %{user: user}} = conn, _) do json(conn, Map.get(user, :domain_blocks, [])) diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex index 7b0b937a2..7fd0562c9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex @@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do %{scopes: ["write:filters"]} when action not in @oauth_read_actions ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "GET /api/v1/filters" def index(%{assigns: %{user: user}} = conn, _) do filters = Filter.get_filters(user) diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex index 1ca86f63f..25f2269b9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do %{scopes: ["follow", "write:follows"]} when action != :index ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "GET /api/v1/follow_requests" def index(%{assigns: %{user: followed}} = conn, _params) do follow_requests = User.get_follow_requests(followed) diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index dac4daa7b..bfe856025 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -11,16 +11,16 @@ defmodule Pleroma.Web.MastodonAPI.ListController do plug(:list_by_id_and_user when action not in [:index, :create]) - plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:index, :show, :list_accounts]) + @oauth_read_actions [:index, :show, :list_accounts] + + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions) plug( OAuthScopesPlug, %{scopes: ["write:lists"]} - when action in [:create, :update, :delete, :add_to_list, :remove_from_list] + when action not in @oauth_read_actions ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) # GET /api/v1/lists diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex index 58e8a30c2..9f9d4574e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do ) plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) # GET /api/v1/markers diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index ac8c18f24..f0492b189 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object]) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) def empty_array(conn, _) do diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 2b6f00952..e36751220 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -15,8 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do plug(OAuthScopesPlug, %{scopes: ["write:media"]}) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "POST /api/v1/media" def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do with {:ok, object} <- diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 7fb536b09..311405277 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -20,8 +20,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action not in @oauth_read_actions) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - # GET /api/v1/notifications def index(conn, %{"account_id" => account_id} = params) do case Pleroma.User.get_cached_by_id(account_id) do diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index d9f894118..af9b66eff 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -22,8 +22,6 @@ defmodule Pleroma.Web.MastodonAPI.PollController do plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index f5782be13..9fbaa7bd1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -11,8 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.ReportController do plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "POST /api/v1/reports" def create(%{assigns: %{user: user}} = conn, params) do with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex index e1e6bd89b..899b78873 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -18,8 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions) plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @doc "GET /api/v1/scheduled_statuses" diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index c258742dd..b54c56967 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search]) def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 397dd10e3..eade83aaf 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -77,7 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do %{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark] ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :show]) + plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]) @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 4647c1f96..d184ea1d0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do action_fallback(:errors) plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(:restrict_push_enabled) # Creates PushSubscription diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index b3c58005e..891f924bc 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -26,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :public) + plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :public) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index 1a09ac62a..4657a4383 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do use Pleroma.Web, :controller + alias Pleroma.ReverseProxy alias Pleroma.Web.MediaProxy diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 60405fbff..d6ffdcbe4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -17,6 +17,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do require Pleroma.Constants + plug(:skip_plug, OAuthScopesPlug when action == :confirmation_resend) + + plug( + :skip_plug, + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :confirmation_resend + ) + plug( OAuthScopesPlug, %{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe] @@ -35,13 +42,8 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) - # An extra safety measure for possible actions not guarded by OAuth permissions specification - plug( - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - when action != :confirmation_resend - ) - plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend) + plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe]) plug(:put_view, Pleroma.Web.MastodonAPI.AccountView) diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 03e95e020..e01825b48 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -1,6 +1,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do use Pleroma.Web, :controller + alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug alias Pleroma.Plugs.OAuthScopesPlug require Logger @@ -11,17 +12,20 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do when action in [ :create, :delete, - :download_from, - :list_from, + :save_from, :import_from_fs, :update_file, :update_metadata ] ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug( + :skip_plug, + [OAuthScopesPlug, ExpectPublicOrAuthenticatedCheckPlug] + when action in [:download_shared, :list_packs, :list_from] + ) - def emoji_dir_path do + defp emoji_dir_path do Path.join( Pleroma.Config.get!([:instance, :static_dir]), "emoji" @@ -212,13 +216,13 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end @doc """ - An admin endpoint to request downloading a pack named `pack_name` from the instance + An admin endpoint to request downloading and storing a pack named `pack_name` from the instance `instance_address`. If the requested instance's admin chose to share the pack, it will be downloaded from that instance, otherwise it will be downloaded from the fallback source, if there is one. """ - def download_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do + def save_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do address = String.trim(address) if shareable_packs_available(address) do diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index d9c1c8636..d4e0d8b7c 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -12,8 +12,6 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show) plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - @doc "GET /api/v1/pleroma/mascot" def show(%{assigns: %{user: user}} = conn, _params) do json(conn, User.get_mascot(user)) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index fe1b97a20..7a65697e8 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -34,12 +34,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug( OAuthScopesPlug, - %{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations] + %{scopes: ["write:conversations"]} + when action in [:update_conversation, :mark_conversations_as_read] ) - plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) - - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug( + OAuthScopesPlug, + %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read + ) def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), @@ -167,7 +169,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end end - def read_conversations(%{assigns: %{user: user}} = conn, _params) do + def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do with {:ok, _, participations} <- Participation.mark_all_as_read(user) do conn |> add_link_headers(participations) @@ -176,7 +178,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end end - def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do + def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do with {:ok, notification} <- Notification.read_one(user, notification_id) do conn |> put_view(NotificationView) @@ -189,7 +191,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end end - def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do + def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do with notifications <- Notification.set_read_up_to(user, max_id) do notifications = Enum.take(notifications, 80) diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index 4463ec477..c81e8535e 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -16,8 +16,6 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles) plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do params = if !params["length"] do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 153802a43..04c1c5941 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -16,6 +16,14 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.UserEnabledPlug) end + pipeline :expect_authentication do + plug(Pleroma.Plugs.ExpectAuthenticatedCheckPlug) + end + + pipeline :expect_public_instance_or_authentication do + plug(Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug) + end + pipeline :authenticate do plug(Pleroma.Plugs.OAuthPlug) plug(Pleroma.Plugs.BasicAuthDecoderPlug) @@ -39,20 +47,22 @@ defmodule Pleroma.Web.Router do end pipeline :api do + plug(:expect_public_instance_or_authentication) plug(:base_api) plug(:after_auth) plug(Pleroma.Plugs.IdempotencyPlug) end pipeline :authenticated_api do + plug(:expect_authentication) plug(:base_api) - plug(Pleroma.Plugs.AuthExpectedPlug) plug(:after_auth) plug(Pleroma.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Plugs.IdempotencyPlug) end pipeline :admin_api do + plug(:expect_authentication) plug(:base_api) plug(Pleroma.Plugs.AdminSecretAuthenticationPlug) plug(:after_auth) @@ -200,24 +210,28 @@ defmodule Pleroma.Web.Router do end scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do + # Modifying packs scope "/packs" do - # Modifying packs pipe_through(:admin_api) post("/import_from_fs", EmojiAPIController, :import_from_fs) - post("/:pack_name/update_file", EmojiAPIController, :update_file) post("/:pack_name/update_metadata", EmojiAPIController, :update_metadata) put("/:name", EmojiAPIController, :create) delete("/:name", EmojiAPIController, :delete) - post("/download_from", EmojiAPIController, :download_from) - post("/list_from", EmojiAPIController, :list_from) + + # Note: /download_from downloads and saves to instance, not to requester + post("/download_from", EmojiAPIController, :save_from) end + # Pack info / downloading scope "/packs" do - # Pack info / downloading get("/", EmojiAPIController, :list_packs) get("/:name/download_shared/", EmojiAPIController, :download_shared) + get("/list_from", EmojiAPIController, :list_from) + + # Deprecated: POST /api/pleroma/emoji/packs/list_from (use GET instead) + post("/list_from", EmojiAPIController, :list_from) end end @@ -277,7 +291,7 @@ defmodule Pleroma.Web.Router do get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) get("/conversations/:id", PleromaAPIController, :conversation) - post("/conversations/read", PleromaAPIController, :read_conversations) + post("/conversations/read", PleromaAPIController, :mark_conversations_as_read) end scope [] do @@ -286,7 +300,7 @@ defmodule Pleroma.Web.Router do patch("/conversations/:id", PleromaAPIController, :update_conversation) put("/statuses/:id/reactions/:emoji", PleromaAPIController, :react_with_emoji) delete("/statuses/:id/reactions/:emoji", PleromaAPIController, :unreact_with_emoji) - post("/notifications/read", PleromaAPIController, :read_notification) + post("/notifications/read", PleromaAPIController, :mark_notifications_as_read) patch("/accounts/update_avatar", AccountController, :update_avatar) patch("/accounts/update_banner", AccountController, :update_banner) @@ -322,53 +336,81 @@ defmodule Pleroma.Web.Router do pipe_through(:authenticated_api) get("/accounts/verify_credentials", AccountController, :verify_credentials) + patch("/accounts/update_credentials", AccountController, :update_credentials) get("/accounts/relationships", AccountController, :relationships) - get("/accounts/:id/lists", AccountController, :lists) get("/accounts/:id/identity_proofs", AccountController, :identity_proofs) - - get("/follow_requests", FollowRequestController, :index) + get("/endorsements", AccountController, :endorsements) get("/blocks", AccountController, :blocks) get("/mutes", AccountController, :mutes) - get("/timelines/home", TimelineController, :home) - get("/timelines/direct", TimelineController, :direct) + post("/follows", AccountController, :follow_by_uri) + post("/accounts/:id/follow", AccountController, :follow) + post("/accounts/:id/unfollow", AccountController, :unfollow) + post("/accounts/:id/block", AccountController, :block) + post("/accounts/:id/unblock", AccountController, :unblock) + post("/accounts/:id/mute", AccountController, :mute) + post("/accounts/:id/unmute", AccountController, :unmute) - get("/favourites", StatusController, :favourites) - get("/bookmarks", StatusController, :bookmarks) + get("/conversations", ConversationController, :index) + post("/conversations/:id/read", ConversationController, :mark_as_read) + + get("/domain_blocks", DomainBlockController, :index) + post("/domain_blocks", DomainBlockController, :create) + delete("/domain_blocks", DomainBlockController, :delete) + + get("/filters", FilterController, :index) + + post("/filters", FilterController, :create) + get("/filters/:id", FilterController, :show) + put("/filters/:id", FilterController, :update) + delete("/filters/:id", FilterController, :delete) + + get("/follow_requests", FollowRequestController, :index) + post("/follow_requests/:id/authorize", FollowRequestController, :authorize) + post("/follow_requests/:id/reject", FollowRequestController, :reject) + + get("/lists", ListController, :index) + get("/lists/:id", ListController, :show) + get("/lists/:id/accounts", ListController, :list_accounts) + + delete("/lists/:id", ListController, :delete) + post("/lists", ListController, :create) + put("/lists/:id", ListController, :update) + post("/lists/:id/accounts", ListController, :add_to_list) + delete("/lists/:id/accounts", ListController, :remove_from_list) + + get("/markers", MarkerController, :index) + post("/markers", MarkerController, :upsert) + + post("/media", MediaController, :create) + put("/media/:id", MediaController, :update) get("/notifications", NotificationController, :index) get("/notifications/:id", NotificationController, :show) + post("/notifications/:id/dismiss", NotificationController, :dismiss) post("/notifications/clear", NotificationController, :clear) delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) # Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead post("/notifications/dismiss", NotificationController, :dismiss) - get("/scheduled_statuses", ScheduledActivityController, :index) - get("/scheduled_statuses/:id", ScheduledActivityController, :show) - - get("/lists", ListController, :index) - get("/lists/:id", ListController, :show) - get("/lists/:id/accounts", ListController, :list_accounts) - - get("/domain_blocks", DomainBlockController, :index) - - get("/filters", FilterController, :index) + post("/polls/:id/votes", PollController, :vote) - get("/suggestions", SuggestionController, :index) + post("/reports", ReportController, :create) - get("/conversations", ConversationController, :index) - post("/conversations/:id/read", ConversationController, :read) + get("/scheduled_statuses", ScheduledActivityController, :index) + get("/scheduled_statuses/:id", ScheduledActivityController, :show) - get("/endorsements", AccountController, :endorsements) + put("/scheduled_statuses/:id", ScheduledActivityController, :update) + delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) - patch("/accounts/update_credentials", AccountController, :update_credentials) + get("/favourites", StatusController, :favourites) + get("/bookmarks", StatusController, :bookmarks) post("/statuses", StatusController, :create) delete("/statuses/:id", StatusController, :delete) - post("/statuses/:id/reblog", StatusController, :reblog) post("/statuses/:id/unreblog", StatusController, :unreblog) post("/statuses/:id/favourite", StatusController, :favourite) @@ -380,49 +422,15 @@ defmodule Pleroma.Web.Router do post("/statuses/:id/mute", StatusController, :mute_conversation) post("/statuses/:id/unmute", StatusController, :unmute_conversation) - put("/scheduled_statuses/:id", ScheduledActivityController, :update) - delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) - - post("/polls/:id/votes", PollController, :vote) - - post("/media", MediaController, :create) - put("/media/:id", MediaController, :update) - - delete("/lists/:id", ListController, :delete) - post("/lists", ListController, :create) - put("/lists/:id", ListController, :update) - - post("/lists/:id/accounts", ListController, :add_to_list) - delete("/lists/:id/accounts", ListController, :remove_from_list) - - post("/filters", FilterController, :create) - get("/filters/:id", FilterController, :show) - put("/filters/:id", FilterController, :update) - delete("/filters/:id", FilterController, :delete) - - post("/reports", ReportController, :create) - - post("/follows", AccountController, :follows) - post("/accounts/:id/follow", AccountController, :follow) - post("/accounts/:id/unfollow", AccountController, :unfollow) - post("/accounts/:id/block", AccountController, :block) - post("/accounts/:id/unblock", AccountController, :unblock) - post("/accounts/:id/mute", AccountController, :mute) - post("/accounts/:id/unmute", AccountController, :unmute) - - post("/follow_requests/:id/authorize", FollowRequestController, :authorize) - post("/follow_requests/:id/reject", FollowRequestController, :reject) - - post("/domain_blocks", DomainBlockController, :create) - delete("/domain_blocks", DomainBlockController, :delete) - post("/push/subscription", SubscriptionController, :create) get("/push/subscription", SubscriptionController, :get) put("/push/subscription", SubscriptionController, :update) delete("/push/subscription", SubscriptionController, :delete) - get("/markers", MarkerController, :index) - post("/markers", MarkerController, :upsert) + get("/suggestions", SuggestionController, :index) + + get("/timelines/home", TimelineController, :home) + get("/timelines/direct", TimelineController, :direct) end scope "/api/web", Pleroma.Web do @@ -507,7 +515,11 @@ 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, :notifications_read) + post( + "/qvitter/statuses/notifications/read", + TwitterAPI.Controller, + :mark_notifications_as_read + ) end pipeline :ostatus do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 537f9f778..9a4c39fa9 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -25,13 +25,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do when action == :follow_import ) - # Note: follower can submit the form (with password auth) not being signed in (having no token) - plug( - OAuthScopesPlug, - %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]} - when action == :do_remote_follow - ) - plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import) plug( diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 31adc2817..55228616a 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -13,12 +13,13 @@ defmodule Pleroma.Web.TwitterAPI.Controller do require Logger - plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + plug( + OAuthScopesPlug, + %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read + ) plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token]) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - action_fallback(:errors) def confirm_email(conn, %{"user_id" => uid, "token" => token}) do @@ -64,7 +65,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do |> send_resp(status, json) end - def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do + 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) @@ -75,7 +79,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do |> render("index.json", %{notifications: notifications, for: user}) end - def notifications_read(%{assigns: %{user: _user}} = conn, _) do + def mark_notifications_as_read(%{assigns: %{user: _user}} = conn, _) do bad_request_reply(conn, "You need to specify latest_id") end diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index bf48ce26c..ec04c05f0 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -2,6 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Web.Plug do + # Substitute for `call/2` which is defined with `use Pleroma.Web, :plug` + @callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t() +end + defmodule Pleroma.Web do @moduledoc """ A module that keeps using definitions for controllers, @@ -20,44 +25,79 @@ defmodule Pleroma.Web do below. """ + alias Pleroma.Plugs.EnsureAuthenticatedPlug + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper + def controller do quote do use Phoenix.Controller, namespace: Pleroma.Web import Plug.Conn + import Pleroma.Web.Gettext import Pleroma.Web.Router.Helpers import Pleroma.Web.TranslationHelpers - alias Pleroma.Plugs.PlugHelper - plug(:set_put_layout) defp set_put_layout(conn, _) do put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) end - # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain - defp skip_plug(conn, plug_module) do - try do - plug_module.skip_plug(conn) - rescue - UndefinedFunctionError -> - raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." - end + # Marks plugs intentionally skipped and blocks their execution if present in plugs chain + defp skip_plug(conn, plug_modules) do + plug_modules + |> List.wrap() + |> Enum.reduce( + conn, + fn plug_module, conn -> + try do + plug_module.skip_plug(conn) + rescue + UndefinedFunctionError -> + raise "`#{plug_module}` is not skippable. Append `use Pleroma.Web, :plug` to its code." + end + end + ) end # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do - with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do + with %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn), + %Plug.Conn{halted: false} <- maybe_perform_authenticated_check(conn), + %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do super(conn, params) end end + # Ensures instance is public -or- user is authenticated if such check was scheduled + defp maybe_perform_public_or_authenticated_check(conn) do + if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do + EnsurePublicOrAuthenticatedPlug.call(conn, %{}) + else + conn + end + end + + # Ensures user is authenticated if such check was scheduled + # Note: runs prior to action even if it was already executed earlier in plug chain + # (since OAuthScopesPlug has option of proceeding unauthenticated) + defp maybe_perform_authenticated_check(conn) do + if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) do + EnsureAuthenticatedPlug.call(conn, %{}) + else + conn + end + end + # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check defp maybe_halt_on_missing_oauth_scopes_check(conn) do - if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && - not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do + if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and + not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do conn |> render_error( :forbidden, @@ -132,7 +172,8 @@ defmodule Pleroma.Web do def plug do quote do - alias Pleroma.Plugs.PlugHelper + @behaviour Pleroma.Web.Plug + @behaviour Plug @doc """ Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain. @@ -146,14 +187,22 @@ defmodule Pleroma.Web do end @impl Plug - @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." + @doc """ + If marked as skipped, returns `conn`, otherwise calls `perform/2`. + Note: multiple invocations of the same plug (with different or same options) are allowed. + """ def call(%Plug.Conn{} = conn, options) do if PlugHelper.plug_skipped?(conn, __MODULE__) do conn else - conn - |> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__) - |> perform(options) + conn = + PlugHelper.append_to_private_list( + conn, + PlugHelper.called_plugs_list_id(), + __MODULE__ + ) + + apply(__MODULE__, :perform, [conn, options]) end end end -- cgit v1.2.3 From f0238d010a61ab935b61beebd5674593a75f17dc Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 21 Apr 2020 23:30:24 +0400 Subject: Improve OpenAPI schema - Removes unneeded wrapping in examples - Adds `:format` attributes --- lib/pleroma/web/api_spec/schemas/account.ex | 152 ++++++++------- .../web/api_spec/schemas/account_create_request.ex | 6 +- .../api_spec/schemas/account_create_response.ex | 12 +- lib/pleroma/web/api_spec/schemas/account_emoji.ex | 18 +- lib/pleroma/web/api_spec/schemas/account_field.ex | 14 +- .../api_spec/schemas/account_field_attribute.ex | 6 +- .../api_spec/schemas/account_follows_request.ex | 2 +- .../web/api_spec/schemas/account_relationship.ex | 26 ++- .../web/api_spec/schemas/app_create_request.ex | 6 +- .../web/api_spec/schemas/app_create_response.ex | 4 +- lib/pleroma/web/api_spec/schemas/list.ex | 6 +- lib/pleroma/web/api_spec/schemas/status.ex | 206 ++++++++++----------- 12 files changed, 225 insertions(+), 233 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index beb093182..3634a7c76 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -17,8 +17,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do type: :object, properties: %{ acct: %Schema{type: :string}, - avatar_static: %Schema{type: :string}, - avatar: %Schema{type: :string}, + avatar_static: %Schema{type: :string, format: :uri}, + avatar: %Schema{type: :string, format: :uri}, bot: %Schema{type: :boolean}, created_at: %Schema{type: :string, format: "date-time"}, display_name: %Schema{type: :string}, @@ -27,13 +27,13 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do follow_requests_count: %Schema{type: :integer}, followers_count: %Schema{type: :integer}, following_count: %Schema{type: :integer}, - header_static: %Schema{type: :string}, - header: %Schema{type: :string}, + header_static: %Schema{type: :string, format: :uri}, + header: %Schema{type: :string, format: :uri}, id: %Schema{type: :string}, locked: %Schema{type: :boolean}, - note: %Schema{type: :string}, + note: %Schema{type: :string, format: :html}, statuses_count: %Schema{type: :integer}, - url: %Schema{type: :string}, + url: %Schema{type: :string, format: :uri}, username: %Schema{type: :string}, pleroma: %Schema{ type: :object, @@ -104,80 +104,78 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do } }, example: %{ - "JSON" => %{ - "acct" => "foobar", - "avatar" => "https://mypleroma.com/images/avi.png", - "avatar_static" => "https://mypleroma.com/images/avi.png", - "bot" => false, - "created_at" => "2020-03-24T13:05:58.000Z", - "display_name" => "foobar", - "emojis" => [], + "acct" => "foobar", + "avatar" => "https://mypleroma.com/images/avi.png", + "avatar_static" => "https://mypleroma.com/images/avi.png", + "bot" => false, + "created_at" => "2020-03-24T13:05:58.000Z", + "display_name" => "foobar", + "emojis" => [], + "fields" => [], + "follow_requests_count" => 0, + "followers_count" => 0, + "following_count" => 1, + "header" => "https://mypleroma.com/images/banner.png", + "header_static" => "https://mypleroma.com/images/banner.png", + "id" => "9tKi3esbG7OQgZ2920", + "locked" => false, + "note" => "cofe", + "pleroma" => %{ + "allow_following_move" => true, + "background_image" => nil, + "confirmation_pending" => true, + "hide_favorites" => true, + "hide_followers" => false, + "hide_followers_count" => false, + "hide_follows" => false, + "hide_follows_count" => false, + "is_admin" => false, + "is_moderator" => false, + "skip_thread_containment" => false, + "chat_token" => + "SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc", + "unread_conversation_count" => 0, + "tags" => [], + "notification_settings" => %{ + "followers" => true, + "follows" => true, + "non_followers" => true, + "non_follows" => true, + "privacy_option" => false + }, + "relationship" => %{ + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => false, + "id" => "9tKi3esbG7OQgZ2920", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false + }, + "settings_store" => %{ + "pleroma-fe" => %{} + } + }, + "source" => %{ "fields" => [], - "follow_requests_count" => 0, - "followers_count" => 0, - "following_count" => 1, - "header" => "https://mypleroma.com/images/banner.png", - "header_static" => "https://mypleroma.com/images/banner.png", - "id" => "9tKi3esbG7OQgZ2920", - "locked" => false, - "note" => "cofe", + "note" => "foobar", "pleroma" => %{ - "allow_following_move" => true, - "background_image" => nil, - "confirmation_pending" => true, - "hide_favorites" => true, - "hide_followers" => false, - "hide_followers_count" => false, - "hide_follows" => false, - "hide_follows_count" => false, - "is_admin" => false, - "is_moderator" => false, - "skip_thread_containment" => false, - "chat_token" => - "SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc", - "unread_conversation_count" => 0, - "tags" => [], - "notification_settings" => %{ - "followers" => true, - "follows" => true, - "non_followers" => true, - "non_follows" => true, - "privacy_option" => false - }, - "relationship" => %{ - "blocked_by" => false, - "blocking" => false, - "domain_blocking" => false, - "endorsed" => false, - "followed_by" => false, - "following" => false, - "id" => "9tKi3esbG7OQgZ2920", - "muting" => false, - "muting_notifications" => false, - "requested" => false, - "showing_reblogs" => true, - "subscribing" => false - }, - "settings_store" => %{ - "pleroma-fe" => %{} - } - }, - "source" => %{ - "fields" => [], - "note" => "foobar", - "pleroma" => %{ - "actor_type" => "Person", - "discoverable" => false, - "no_rich_text" => false, - "show_role" => true - }, - "privacy" => "public", - "sensitive" => false + "actor_type" => "Person", + "discoverable" => false, + "no_rich_text" => false, + "show_role" => true }, - "statuses_count" => 0, - "url" => "https://mypleroma.com/users/foobar", - "username" => "foobar" - } + "privacy" => "public", + "sensitive" => false + }, + "statuses_count" => 0, + "url" => "https://mypleroma.com/users/foobar", + "username" => "foobar" } }) end diff --git a/lib/pleroma/web/api_spec/schemas/account_create_request.ex b/lib/pleroma/web/api_spec/schemas/account_create_request.ex index 398e2d613..49fa12159 100644 --- a/lib/pleroma/web/api_spec/schemas/account_create_request.ex +++ b/lib/pleroma/web/api_spec/schemas/account_create_request.ex @@ -23,7 +23,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest do "The email address to be used for login. Required when `account_activation_required` is enabled.", format: :email }, - password: %Schema{type: :string, description: "The password to be used for login"}, + password: %Schema{ + type: :string, + description: "The password to be used for login", + format: :password + }, agreement: %Schema{ type: :boolean, description: diff --git a/lib/pleroma/web/api_spec/schemas/account_create_response.ex b/lib/pleroma/web/api_spec/schemas/account_create_response.ex index f41a034c0..2237351a2 100644 --- a/lib/pleroma/web/api_spec/schemas/account_create_response.ex +++ b/lib/pleroma/web/api_spec/schemas/account_create_response.ex @@ -15,15 +15,13 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse do token_type: %Schema{type: :string}, access_token: %Schema{type: :string}, scope: %Schema{type: :array, items: %Schema{type: :string}}, - created_at: %Schema{type: :integer} + created_at: %Schema{type: :integer, format: :"date-time"} }, example: %{ - "JSON" => %{ - "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk", - "created_at" => 1_585_918_714, - "scope" => ["read", "write", "follow", "push"], - "token_type" => "Bearer" - } + "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk", + "created_at" => 1_585_918_714, + "scope" => ["read", "write", "follow", "push"], + "token_type" => "Bearer" } }) end diff --git a/lib/pleroma/web/api_spec/schemas/account_emoji.ex b/lib/pleroma/web/api_spec/schemas/account_emoji.ex index 403b13b15..6c1d4d95c 100644 --- a/lib/pleroma/web/api_spec/schemas/account_emoji.ex +++ b/lib/pleroma/web/api_spec/schemas/account_emoji.ex @@ -13,19 +13,17 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountEmoji do type: :object, properties: %{ shortcode: %Schema{type: :string}, - url: %Schema{type: :string}, - static_url: %Schema{type: :string}, + url: %Schema{type: :string, format: :uri}, + static_url: %Schema{type: :string, format: :uri}, visible_in_picker: %Schema{type: :boolean} }, example: %{ - "JSON" => %{ - "shortcode" => "fatyoshi", - "url" => - "https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png", - "static_url" => - "https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png", - "visible_in_picker" => true - } + "shortcode" => "fatyoshi", + "url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png", + "static_url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png", + "visible_in_picker" => true } }) end diff --git a/lib/pleroma/web/api_spec/schemas/account_field.ex b/lib/pleroma/web/api_spec/schemas/account_field.ex index 8906d812d..fa97073a0 100644 --- a/lib/pleroma/web/api_spec/schemas/account_field.ex +++ b/lib/pleroma/web/api_spec/schemas/account_field.ex @@ -13,16 +13,14 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do type: :object, properties: %{ name: %Schema{type: :string}, - value: %Schema{type: :string}, - verified_at: %Schema{type: :string, format: "date-time", nullable: true} + value: %Schema{type: :string, format: :html}, + verified_at: %Schema{type: :string, format: :"date-time", nullable: true} }, example: %{ - "JSON" => %{ - "name" => "Website", - "value" => - "https://pleroma.com", - "verified_at" => "2019-08-29T04:14:55.571+00:00" - } + "name" => "Website", + "value" => + "https://pleroma.com", + "verified_at" => "2019-08-29T04:14:55.571+00:00" } }) end diff --git a/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex b/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex index fbbdf95f5..89e483655 100644 --- a/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex +++ b/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex @@ -17,10 +17,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountAttributeField do }, required: [:name, :value], example: %{ - "JSON" => %{ - "name" => "Website", - "value" => "https://pleroma.com" - } + "name" => "Website", + "value" => "https://pleroma.com" } }) end diff --git a/lib/pleroma/web/api_spec/schemas/account_follows_request.ex b/lib/pleroma/web/api_spec/schemas/account_follows_request.ex index 4fbe615d6..19dce0cb2 100644 --- a/lib/pleroma/web/api_spec/schemas/account_follows_request.ex +++ b/lib/pleroma/web/api_spec/schemas/account_follows_request.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest do description: "POST body for muting an account", type: :object, properties: %{ - uri: %Schema{type: :string} + uri: %Schema{type: :string, format: :uri} }, required: [:uri] }) diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 7db3b49bb..f2bd37d39 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -26,20 +26,18 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do subscribing: %Schema{type: :boolean} }, example: %{ - "JSON" => %{ - "blocked_by" => false, - "blocking" => false, - "domain_blocking" => false, - "endorsed" => false, - "followed_by" => false, - "following" => false, - "id" => "9tKi3esbG7OQgZ2920", - "muting" => false, - "muting_notifications" => false, - "requested" => false, - "showing_reblogs" => true, - "subscribing" => false - } + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => false, + "id" => "9tKi3esbG7OQgZ2920", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false } }) end diff --git a/lib/pleroma/web/api_spec/schemas/app_create_request.ex b/lib/pleroma/web/api_spec/schemas/app_create_request.ex index 8a83abef3..7e92205cf 100644 --- a/lib/pleroma/web/api_spec/schemas/app_create_request.ex +++ b/lib/pleroma/web/api_spec/schemas/app_create_request.ex @@ -21,7 +21,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateRequest do type: :string, description: "Space separated list of scopes. If none is provided, defaults to `read`." }, - website: %Schema{type: :string, description: "A URL to the homepage of your app"} + website: %Schema{ + type: :string, + description: "A URL to the homepage of your app", + format: :uri + } }, required: [:client_name, :redirect_uris], example: %{ diff --git a/lib/pleroma/web/api_spec/schemas/app_create_response.ex b/lib/pleroma/web/api_spec/schemas/app_create_response.ex index f290fb031..3c41d4ee5 100644 --- a/lib/pleroma/web/api_spec/schemas/app_create_response.ex +++ b/lib/pleroma/web/api_spec/schemas/app_create_response.ex @@ -16,9 +16,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateResponse do name: %Schema{type: :string}, client_id: %Schema{type: :string}, client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, + redirect_uri: %Schema{type: :string, format: :uri}, vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} + website: %Schema{type: :string, nullable: true, format: :uri} }, example: %{ "id" => "123", diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex index 30fa7db93..f85fac2b8 100644 --- a/lib/pleroma/web/api_spec/schemas/list.ex +++ b/lib/pleroma/web/api_spec/schemas/list.ex @@ -16,10 +16,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.List do title: %Schema{type: :string} }, example: %{ - "JSON" => %{ - "id" => "123", - "title" => "my list" - } + "id" => "123", + "title" => "my list" } }) end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 486c3a0fe..a022450e6 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :object, properties: %{ name: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} + website: %Schema{type: :string, nullable: true, format: :uri} } }, bookmarked: %Schema{type: :boolean}, @@ -29,16 +29,16 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :object, nullable: true, properties: %{ - type: %Schema{type: :string}, - provider_name: %Schema{type: :string}, - provider_url: %Schema{type: :string}, - url: %Schema{type: :string}, - image: %Schema{type: :string}, + type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]}, + provider_name: %Schema{type: :string, nullable: true}, + provider_url: %Schema{type: :string, format: :uri}, + url: %Schema{type: :string, format: :uri}, + image: %Schema{type: :string, nullable: true, format: :uri}, title: %Schema{type: :string}, description: %Schema{type: :string} } }, - content: %Schema{type: :string}, + content: %Schema{type: :string, format: :html}, created_at: %Schema{type: :string, format: "date-time"}, emojis: %Schema{type: :array, items: AccountEmoji}, favourited: %Schema{type: :boolean}, @@ -53,10 +53,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :object, properties: %{ id: %Schema{type: :string}, - url: %Schema{type: :string}, - remote_url: %Schema{type: :string}, - preview_url: %Schema{type: :string}, - text_url: %Schema{type: :string}, + url: %Schema{type: :string, format: :uri}, + remote_url: %Schema{type: :string, format: :uri}, + preview_url: %Schema{type: :string, format: :uri}, + text_url: %Schema{type: :string, format: :uri}, description: %Schema{type: :string}, type: %Schema{type: :string, enum: ["image", "video", "audio", "unknown"]}, pleroma: %Schema{ @@ -74,7 +74,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do id: %Schema{type: :string}, acct: %Schema{type: :string}, username: %Schema{type: :string}, - url: %Schema{type: :string} + url: %Schema{type: :string, format: :uri} } } }, @@ -120,108 +120,106 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :object, properties: %{ name: %Schema{type: :string}, - url: %Schema{type: :string} + url: %Schema{type: :string, format: :uri} } } }, - uri: %Schema{type: :string}, - url: %Schema{type: :string}, + uri: %Schema{type: :string, format: :uri}, + url: %Schema{type: :string, nullable: true, format: :uri}, visibility: VisibilityScope }, example: %{ - "JSON" => %{ - "account" => %{ - "acct" => "nick6", - "avatar" => "http://localhost:4001/images/avi.png", - "avatar_static" => "http://localhost:4001/images/avi.png", - "bot" => false, - "created_at" => "2020-04-07T19:48:51.000Z", - "display_name" => "Test テスト User 6", - "emojis" => [], - "fields" => [], - "followers_count" => 1, - "following_count" => 0, - "header" => "http://localhost:4001/images/banner.png", - "header_static" => "http://localhost:4001/images/banner.png", - "id" => "9toJCsKN7SmSf3aj5c", - "locked" => false, - "note" => "Tester Number 6", - "pleroma" => %{ - "background_image" => nil, - "confirmation_pending" => false, - "hide_favorites" => true, - "hide_followers" => false, - "hide_followers_count" => false, - "hide_follows" => false, - "hide_follows_count" => false, - "is_admin" => false, - "is_moderator" => false, - "relationship" => %{ - "blocked_by" => false, - "blocking" => false, - "domain_blocking" => false, - "endorsed" => false, - "followed_by" => false, - "following" => true, - "id" => "9toJCsKN7SmSf3aj5c", - "muting" => false, - "muting_notifications" => false, - "requested" => false, - "showing_reblogs" => true, - "subscribing" => false - }, - "skip_thread_containment" => false, - "tags" => [] - }, - "source" => %{ - "fields" => [], - "note" => "Tester Number 6", - "pleroma" => %{"actor_type" => "Person", "discoverable" => false}, - "sensitive" => false - }, - "statuses_count" => 1, - "url" => "http://localhost:4001/users/nick6", - "username" => "nick6" - }, - "application" => %{"name" => "Web", "website" => nil}, - "bookmarked" => false, - "card" => nil, - "content" => "foobar", + "account" => %{ + "acct" => "nick6", + "avatar" => "http://localhost:4001/images/avi.png", + "avatar_static" => "http://localhost:4001/images/avi.png", + "bot" => false, "created_at" => "2020-04-07T19:48:51.000Z", + "display_name" => "Test テスト User 6", "emojis" => [], - "favourited" => false, - "favourites_count" => 0, - "id" => "9toJCu5YZW7O7gfvH6", - "in_reply_to_account_id" => nil, - "in_reply_to_id" => nil, - "language" => nil, - "media_attachments" => [], - "mentions" => [], - "muted" => false, - "pinned" => false, + "fields" => [], + "followers_count" => 1, + "following_count" => 0, + "header" => "http://localhost:4001/images/banner.png", + "header_static" => "http://localhost:4001/images/banner.png", + "id" => "9toJCsKN7SmSf3aj5c", + "locked" => false, + "note" => "Tester Number 6", "pleroma" => %{ - "content" => %{"text/plain" => "foobar"}, - "conversation_id" => 345_972, - "direct_conversation_id" => nil, - "emoji_reactions" => [], - "expires_at" => nil, - "in_reply_to_account_acct" => nil, - "local" => true, - "spoiler_text" => %{"text/plain" => ""}, - "thread_muted" => false + "background_image" => nil, + "confirmation_pending" => false, + "hide_favorites" => true, + "hide_followers" => false, + "hide_followers_count" => false, + "hide_follows" => false, + "hide_follows_count" => false, + "is_admin" => false, + "is_moderator" => false, + "relationship" => %{ + "blocked_by" => false, + "blocking" => false, + "domain_blocking" => false, + "endorsed" => false, + "followed_by" => false, + "following" => true, + "id" => "9toJCsKN7SmSf3aj5c", + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "showing_reblogs" => true, + "subscribing" => false + }, + "skip_thread_containment" => false, + "tags" => [] }, - "poll" => nil, - "reblog" => nil, - "reblogged" => false, - "reblogs_count" => 0, - "replies_count" => 0, - "sensitive" => false, - "spoiler_text" => "", - "tags" => [], - "uri" => "http://localhost:4001/objects/0f5dad44-0e9e-4610-b377-a2631e499190", - "url" => "http://localhost:4001/notice/9toJCu5YZW7O7gfvH6", - "visibility" => "private" - } + "source" => %{ + "fields" => [], + "note" => "Tester Number 6", + "pleroma" => %{"actor_type" => "Person", "discoverable" => false}, + "sensitive" => false + }, + "statuses_count" => 1, + "url" => "http://localhost:4001/users/nick6", + "username" => "nick6" + }, + "application" => %{"name" => "Web", "website" => nil}, + "bookmarked" => false, + "card" => nil, + "content" => "foobar", + "created_at" => "2020-04-07T19:48:51.000Z", + "emojis" => [], + "favourited" => false, + "favourites_count" => 0, + "id" => "9toJCu5YZW7O7gfvH6", + "in_reply_to_account_id" => nil, + "in_reply_to_id" => nil, + "language" => nil, + "media_attachments" => [], + "mentions" => [], + "muted" => false, + "pinned" => false, + "pleroma" => %{ + "content" => %{"text/plain" => "foobar"}, + "conversation_id" => 345_972, + "direct_conversation_id" => nil, + "emoji_reactions" => [], + "expires_at" => nil, + "in_reply_to_account_acct" => nil, + "local" => true, + "spoiler_text" => %{"text/plain" => ""}, + "thread_muted" => false + }, + "poll" => nil, + "reblog" => nil, + "reblogged" => false, + "reblogs_count" => 0, + "replies_count" => 0, + "sensitive" => false, + "spoiler_text" => "", + "tags" => [], + "uri" => "http://localhost:4001/objects/0f5dad44-0e9e-4610-b377-a2631e499190", + "url" => "http://localhost:4001/notice/9toJCu5YZW7O7gfvH6", + "visibility" => "private" } }) end -- cgit v1.2.3 From 11433cd38d9761ddf3fdb94f8c39526910b975c1 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 21 Apr 2020 23:54:45 +0400 Subject: Add OpenAPI schema for FlakeID --- lib/pleroma/web/api_spec/schemas/account.ex | 22 ++++------------------ .../web/api_spec/schemas/account_relationship.ex | 3 ++- lib/pleroma/web/api_spec/schemas/flake_id.ex | 14 ++++++++++++++ lib/pleroma/web/api_spec/schemas/poll.ex | 3 ++- lib/pleroma/web/api_spec/schemas/status.ex | 3 ++- 5 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/flake_id.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index 3634a7c76..f57015254 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -6,7 +6,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji alias Pleroma.Web.ApiSpec.Schemas.AccountField + alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope require OpenApiSpex @@ -29,7 +31,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do following_count: %Schema{type: :integer}, header_static: %Schema{type: :string, format: :uri}, header: %Schema{type: :string, format: :uri}, - id: %Schema{type: :string}, + id: FlakeID, locked: %Schema{type: :boolean}, note: %Schema{type: :string, format: :html}, statuses_count: %Schema{type: :integer}, @@ -62,23 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do privacy_option: %Schema{type: :boolean} } }, - relationship: %Schema{ - type: :object, - properties: %{ - blocked_by: %Schema{type: :boolean}, - blocking: %Schema{type: :boolean}, - domain_blocking: %Schema{type: :boolean}, - endorsed: %Schema{type: :boolean}, - followed_by: %Schema{type: :boolean}, - following: %Schema{type: :boolean}, - id: %Schema{type: :string}, - muting: %Schema{type: :boolean}, - muting_notifications: %Schema{type: :boolean}, - requested: %Schema{type: :boolean}, - showing_reblogs: %Schema{type: :boolean}, - subscribing: %Schema{type: :boolean} - } - }, + relationship: AccountRelationship, settings_store: %Schema{ type: :object } diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index f2bd37d39..8b982669e 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.FlakeID require OpenApiSpex @@ -18,7 +19,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do endorsed: %Schema{type: :boolean}, followed_by: %Schema{type: :boolean}, following: %Schema{type: :boolean}, - id: %Schema{type: :string}, + id: FlakeID, muting: %Schema{type: :boolean}, muting_notifications: %Schema{type: :boolean}, requested: %Schema{type: :boolean}, diff --git a/lib/pleroma/web/api_spec/schemas/flake_id.ex b/lib/pleroma/web/api_spec/schemas/flake_id.ex new file mode 100644 index 000000000..b8e03b8a1 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/flake_id.ex @@ -0,0 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "FlakeID", + description: + "Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings", + type: :string + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index 2a9975f85..5fc9e889f 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.FlakeID require OpenApiSpex @@ -13,7 +14,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do description: "Response schema for account custom fields", type: :object, properties: %{ - id: %Schema{type: :string}, + id: FlakeID, expires_at: %Schema{type: :string, format: "date-time"}, expired: %Schema{type: :boolean}, multiple: %Schema{type: :boolean}, diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index a022450e6..bf5f04691 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.Poll alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -43,7 +44,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do emojis: %Schema{type: :array, items: AccountEmoji}, favourited: %Schema{type: :boolean}, favourites_count: %Schema{type: :integer}, - id: %Schema{type: :string}, + id: FlakeID, in_reply_to_account_id: %Schema{type: :string, nullable: true}, in_reply_to_id: %Schema{type: :string, nullable: true}, language: %Schema{type: :string, nullable: true}, -- cgit v1.2.3 From f719a5b23a9bec4ed94f36c07e24aa1413654bae Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 13:28:34 +0200 Subject: WebPush: Return proper values for jobs. --- lib/pleroma/web/push/impl.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index f1740a6e0..a9f893f7b 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -55,11 +55,12 @@ defmodule Pleroma.Web.Push.Impl do |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end + |> (&{:ok, &1}).() end def perform(_) do Logger.warn("Unknown notification type") - :error + {:error, :unknown_type} end @doc "Push message to web" -- cgit v1.2.3 From 923513b6417973f700a80ee969c6c92ed2c9faee Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 13:28:52 +0200 Subject: Federator: Return proper values for jobs --- lib/pleroma/web/federator/federator.ex | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index fd904ef0a..f5803578d 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -72,19 +72,24 @@ defmodule Pleroma.Web.Federator do # actor shouldn't be acting on objects outside their own AP server. with {:ok, _user} <- ap_enabled_actor(params["actor"]), nil <- Activity.normalize(params["id"]), - :ok <- Containment.contain_origin_from_id(params["actor"], params), + {_, :ok} <- + {:correct_origin?, Containment.contain_origin_from_id(params["actor"], params)}, {:ok, activity} <- Transmogrifier.handle_incoming(params) do {:ok, activity} else + {:correct_origin?, _} -> + Logger.debug("Origin containment failure for #{params["id"]}") + {:error, :origin_containment_failed} + %Activity{} -> Logger.debug("Already had #{params["id"]}") - :error + {:error, :already_present} - _e -> + e -> # Just drop those for now Logger.debug("Unhandled activity") Logger.debug(Jason.encode!(params, pretty: true)) - :error + {:error, e} end end -- cgit v1.2.3 From 5102468d0f8067548ef233b5947da4bc517f5774 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 14:06:39 +0200 Subject: Polls: Persist and show voters' count --- lib/pleroma/object.ex | 5 ++++- lib/pleroma/web/activity_pub/activity_pub.ex | 5 +++-- lib/pleroma/web/mastodon_api/views/poll_view.ex | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 9574432f0..e678fd415 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -261,7 +261,7 @@ defmodule Pleroma.Object do end end - def increase_vote_count(ap_id, name) do + def increase_vote_count(ap_id, name, actor) do with %Object{} = object <- Object.normalize(ap_id), "Question" <- object.data["type"] do multiple = Map.has_key?(object.data, "anyOf") @@ -276,12 +276,15 @@ defmodule Pleroma.Object do option end) + voters = [actor | object.data["voters"] || []] |> Enum.uniq() + data = if multiple do Map.put(object.data, "anyOf", options) else Map.put(object.data, "oneOf", options) end + |> Map.put("voters", voters) object |> Object.change(%{data: data}) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index eedea08a2..4a133498e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -118,9 +118,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def increase_poll_votes_if_vote(%{ "object" => %{"inReplyTo" => reply_ap_id, "name" => name}, - "type" => "Create" + "type" => "Create", + "actor" => actor }) do - Object.increase_vote_count(reply_ap_id, name) + Object.increase_vote_count(reply_ap_id, name, actor) end def increase_poll_votes_if_vote(_create_data), do: :noop diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 40edbb213..59a5deb28 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.PollView do expired: expired, multiple: multiple, votes_count: votes_count, + voters_count: (multiple || nil) && voters_count(object), options: options, voted: voted?(params), emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"]) @@ -62,6 +63,12 @@ defmodule Pleroma.Web.MastodonAPI.PollView do end) end + defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do + length(voters) + end + + defp voters_count(_), do: 0 + defp voted?(%{object: object} = opts) do if opts[:for] do existing_votes = Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object) -- cgit v1.2.3 From c10485db163d56acd7206980f91f0e51153ef36a Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 14:26:19 +0200 Subject: StatusController: Ignore nil scheduled_at parameters. --- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 397dd10e3..f6e4f7d66 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -127,7 +127,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do def create( %{assigns: %{user: user}} = conn, %{"status" => _, "scheduled_at" => scheduled_at} = params - ) do + ) + when not is_nil(scheduled_at) do params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"]) with {:far_enough, true} <- {:far_enough, ScheduledActivity.far_enough?(scheduled_at)}, -- cgit v1.2.3 From 5b3952619818d38f8fdba9a64b050ce3f24394ff Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 15:04:26 +0200 Subject: AccountController: Use code 400 for self-follow. --- .../mastodon_api/controllers/account_controller.ex | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index e8e59ac66..5a92cebd8 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -293,7 +293,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/follow" def follow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do - {:error, :not_found} + {:error, "Can not follow yourself"} end def follow(%{assigns: %{user: follower, account: followed}} = conn, _params) do @@ -306,7 +306,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/unfollow" def unfollow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do - {:error, :not_found} + {:error, "Can not unfollow yourself"} end def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) do @@ -356,14 +356,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/follows" - def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do - with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, - {_, true} <- {:followed, follower.id != followed.id}, - {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do - render(conn, "show.json", user: followed, for: follower) - else - {:followed, _} -> {:error, :not_found} - {:error, message} -> json_response(conn, :forbidden, %{error: message}) + def follows(conn, %{"uri" => uri}) do + case User.get_cached_by_nickname(uri) do + %User{} = user -> + conn + |> assign(:account, user) + |> follow(%{}) + + nil -> + {:error, :not_found} end end -- cgit v1.2.3 From 8b88e2a6e2b3a777ca99bf94676ab47f2d4cc0ea Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 15:31:37 +0200 Subject: Stats: Ignore internal users for user count. --- lib/pleroma/stats.ex | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 4446562ac..6763786a7 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -45,11 +45,11 @@ defmodule Pleroma.Stats do end def init(_args) do - {:ok, get_stat_data()} + {:ok, calculate_stat_data()} end def handle_call(:force_update, _from, _state) do - new_stats = get_stat_data() + new_stats = calculate_stat_data() {:reply, new_stats, new_stats} end @@ -58,12 +58,12 @@ defmodule Pleroma.Stats do end def handle_cast(:run_update, _state) do - new_stats = get_stat_data() + new_stats = calculate_stat_data() {:noreply, new_stats} end - defp get_stat_data do + def calculate_stat_data do peers = from( u in User, @@ -77,7 +77,16 @@ defmodule Pleroma.Stats do status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count) - user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) + users_query = + from(u in User, + where: u.deactivated != true, + where: u.local == true, + where: not is_nil(u.nickname), + where: fragment("? not like 'internal.%'", u.nickname), + where: fragment("? not like '%/relay'", u.ap_id) + ) + + user_count = Repo.aggregate(users_query, :count, :id) %{ peers: peers, -- cgit v1.2.3 From 452072ec95781214df262962b71c60b7d771b7b1 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Wed, 22 Apr 2020 16:02:40 +0200 Subject: static_fe: Add microformats2 classes --- .../web/templates/static_fe/static_fe/_attachment.html.eex | 6 +++--- lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex | 10 +++++++--- .../web/templates/static_fe/static_fe/_user_card.html.eex | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex index 7e04e9550..4853e7f4b 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/_attachment.html.eex @@ -1,8 +1,8 @@ <%= case @mediaType do %> <% "audio" -> %> - + <% "video" -> %> - + <% _ -> %> -<%= @name %> +<%= @name %> <% end %> diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex index df5e5eedd..df0244795 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex @@ -1,12 +1,16 @@ -
id="selected" <% end %>> +
id="selected" <% end %>>

- <%= link format_date(@published), to: @link, class: "activity-link" %> + + +

<%= render("_user_card.html", %{user: @user}) %>
<%= if @title != "" do %>
open<% end %>> - <%= raw @title %> + <%= raw @title %>
<%= raw @content %>
<% else %> diff --git a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex index 56f3a1524..977b894d3 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/_user_card.html.eex @@ -1,10 +1,10 @@
- +
- <%= raw Formatter.emojify(@user.name, @user.emoji) %> + <%= raw Formatter.emojify(@user.name, @user.emoji) %> <%= @user.nickname %>
-- cgit v1.2.3 From 7a3a88a13ef526fba18bb6aeadc93f5da934dc5b Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 17:21:13 +0200 Subject: Streamer: Stream boosts to the boosting user. --- lib/pleroma/user.ex | 4 +++- lib/pleroma/web/streamer/worker.ex | 18 ------------------ 2 files changed, 3 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index bef4679cb..477237756 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1180,7 +1180,9 @@ defmodule Pleroma.User do end @spec get_recipients_from_activity(Activity.t()) :: [User.t()] - def get_recipients_from_activity(%Activity{recipients: to}) do + def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do + to = [actor | to] + User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false}) |> Repo.all() end diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index abfed21c8..f6160fa4d 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -158,24 +158,6 @@ defmodule Pleroma.Web.Streamer.Worker do should_send?(user, activity) end - def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do - Enum.each(topics[topic] || [], fn %StreamerSocket{ - transport_pid: transport_pid, - user: socket_user - } -> - # Get the current user so we have up-to-date blocks etc. - if socket_user do - user = User.get_cached_by_ap_id(socket_user.ap_id) - - if should_send?(user, item) do - send(transport_pid, {:text, StreamerView.render("update.json", item, user)}) - end - else - send(transport_pid, {:text, StreamerView.render("update.json", item)}) - end - end) - end - def push_to_socket(topics, topic, %Participation{} = participation) do Enum.each(topics[topic] || [], fn %StreamerSocket{transport_pid: transport_pid} -> send(transport_pid, {:text, StreamerView.render("conversation.json", participation)}) -- cgit v1.2.3 From 88b82e5c3edae649f1caa45c6ef805828e4b8b1e Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 21 Apr 2020 20:05:25 +0400 Subject: Fix follow import --- .../web/twitter_api/controllers/util_controller.ex | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 537f9f778..824951d59 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -199,15 +199,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do - with lines <- String.split(list, "\n"), - followed_identifiers <- - Enum.map(lines, fn line -> - String.split(line, ",") |> List.first() - end) - |> List.delete("Account address") do - User.follow_import(follower, followed_identifiers) - json(conn, "job started") - end + followed_identifiers = + list + |> String.split("\n") + |> Enum.map(&(&1 |> String.split(",") |> List.first())) + |> List.delete("Account address") + |> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@"))) + |> Enum.reject(&(&1 == "")) + + User.follow_import(follower, followed_identifiers) + json(conn, "job started") end def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do @@ -215,10 +216,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do - with blocked_identifiers <- String.split(list) do - User.blocks_import(blocker, blocked_identifiers) - json(conn, "job started") - end + User.blocks_import(blocker, _blocked_identifiers = String.split(list)) + json(conn, "job started") end def change_password(%{assigns: %{user: user}} = conn, params) do -- cgit v1.2.3 From e7771424a895572626ec36a98bf862952dc6ff07 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 22 Apr 2020 18:13:13 +0400 Subject: Fix blocks import --- lib/pleroma/web/twitter_api/controllers/util_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 824951d59..d5d5ce08f 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -216,7 +216,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do - User.blocks_import(blocker, _blocked_identifiers = String.split(list)) + blocked_identifiers = list |> String.split() |> Enum.map(&String.trim_leading(&1, "@")) + User.blocks_import(blocker, blocked_identifiers) json(conn, "job started") end -- cgit v1.2.3 From 6db52c3b3680efbdb56d53d84d5dec0f4b6e34f0 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 22 Apr 2020 19:00:08 +0400 Subject: Fix Oban warning Warning example: [warn] Expected Elixir.Pleroma.Workers.BackgroundWorker.perform/2 to return :ok, {:ok, value}, or {:error, reason}. Instead received: [error: "not found @user@server.party", error: "not found "] The job will be considered a success. --- lib/pleroma/workers/background_worker.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 0f8ece2c4..57c3a9c3a 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -35,7 +35,7 @@ defmodule Pleroma.Workers.BackgroundWorker do _job ) do blocker = User.get_cached_by_id(blocker_id) - User.perform(:blocks_import, blocker, blocked_identifiers) + {:ok, User.perform(:blocks_import, blocker, blocked_identifiers)} end def perform( @@ -47,7 +47,7 @@ defmodule Pleroma.Workers.BackgroundWorker do _job ) do follower = User.get_cached_by_id(follower_id) - User.perform(:follow_import, follower, followed_identifiers) + {:ok, User.perform(:follow_import, follower, followed_identifiers)} end def perform(%{"op" => "media_proxy_preload", "message" => message}, _job) do -- cgit v1.2.3 From b03aeae8b935b0a211c51cc3be5f1c15591f5a2f Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 22 Apr 2020 15:31:41 +0000 Subject: Apply suggestion to lib/pleroma/notification.ex --- lib/pleroma/notification.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index d305a43ba..aaa675253 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -362,7 +362,7 @@ defmodule Pleroma.Notification do def get_notified_from_activity(_, _local_only), do: {[], []} - @doc "Filters out AP IDs of users who domain-block and not follow activity actor" + @doc "Filters out AP IDs domain-blocking and not following the activity's actor" def exclude_domain_blocker_ap_ids(ap_ids, activity, preloaded_users \\ []) def exclude_domain_blocker_ap_ids([], _activity, _preloaded_users), do: [] -- cgit v1.2.3 From 2958a7d246f40141a88bcb7bdd6a477c4f65f0bc Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 22 Apr 2020 18:50:25 +0300 Subject: Fixed OAuth restrictions for :api routes. Made auth info dropped for :api routes if OAuth check was neither performed nor explicitly skipped. --- lib/pleroma/plugs/oauth_scopes_plug.ex | 13 +++++++-- .../mastodon_api/controllers/account_controller.ex | 4 +-- .../web/mastodon_api/controllers/app_controller.ex | 8 +++++ .../controllers/custom_emoji_controller.ex | 6 ++++ .../controllers/instance_controller.ex | 6 ++++ .../controllers/mastodon_api_controller.ex | 6 +++- .../mastodon_api/controllers/search_controller.ex | 2 ++ .../controllers/timeline_controller.ex | 17 +++++++---- .../controllers/pleroma_api_controller.ex | 6 ++++ .../pleroma_api/controllers/scrobble_controller.ex | 6 +++- lib/pleroma/web/router.ex | 34 ++++++++++------------ .../web/twitter_api/twitter_api_controller.ex | 6 ++-- lib/pleroma/web/web.ex | 14 ++++++++- 13 files changed, 93 insertions(+), 35 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index a61582566..efc25b79f 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -28,9 +28,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do conn options[:fallback] == :proceed_unauthenticated -> - conn - |> assign(:user, nil) - |> assign(:token, nil) + drop_auth_info(conn) true -> missing_scopes = scopes -- matched_scopes @@ -46,6 +44,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do end end + @doc "Drops authentication info from connection" + def drop_auth_info(conn) do + # To simplify debugging, setting a private variable on `conn` if auth info is dropped + conn + |> put_private(:authentication_ignored, true) + |> assign(:user, nil) + |> assign(:token, nil) + end + @doc "Filters descendants of supported scopes" def filter_descendants(scopes, supported_scopes) do Enum.filter( diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 9b8cc0d0d..e501b3555 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} - when action in [:show, :endorsements] + when action in [:show, :followers, :following, :endorsements] ) plug( @@ -49,7 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, %{scopes: ["read:accounts"]} - when action in [:endorsements, :verify_credentials, :followers, :following] + when action in [:endorsements, :verify_credentials] ) plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 005c60444..408e11474 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo alias Pleroma.Web.OAuth.App @@ -13,7 +14,14 @@ defmodule Pleroma.Web.MastodonAPI.AppController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug( + :skip_plug, + [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] + when action == :create + ) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials) + plug(OpenApiSpex.Plug.CastAndValidate) @local_mastodon_name "Mastodon-Local" diff --git a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex index 3bfebef8b..000ad743f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex @@ -7,6 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do plug(OpenApiSpex.Plug.CastAndValidate) + plug( + :skip_plug, + [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + when action == :index + ) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation def index(conn, _params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex index 27b5b1a52..237f85677 100644 --- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex @@ -5,6 +5,12 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do use Pleroma.Web, :controller + plug( + :skip_plug, + [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + when action in [:show, :peers] + ) + @doc "GET /api/v1/instance" def show(conn, _params) do render(conn, "show.json") diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index f0492b189..e7767de4e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -15,7 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger - plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object]) + plug( + :skip_plug, + [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + when action in [:empty_array, :empty_object] + ) action_fallback(Pleroma.Web.MastodonAPI.FallbackController) diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index b54c56967..cd49da6ad 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -21,6 +21,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) + # Note: on private instances auth is required (EnsurePublicOrAuthenticatedPlug is not skipped) + plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search]) def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 891f924bc..040a0b9dd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1] alias Pleroma.Pagination + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.User @@ -26,7 +27,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list) - plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :public) + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} + when action in [:public, :hashtag] + ) + + plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag]) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) @@ -93,7 +100,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key]) - if not (restrict? and is_nil(user)) do + if restrict? and is_nil(user) do + render_error(conn, :unauthorized, "authorization required for timeline view") + else activities = params |> Map.put("type", ["Create", "Announce"]) @@ -110,12 +119,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do as: :activity, skip_relationships: skip_relationships?(params) ) - else - render_error(conn, :unauthorized, "authorization required for timeline view") end end - def hashtag_fetching(params, user, local_only) do + defp hashtag_fetching(params, user, local_only) do tags = [params["tag"], params["any"]] |> List.flatten() diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 7a65697e8..2c1874051 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -26,6 +26,12 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do when action in [:conversation, :conversation_statuses] ) + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} + when action == :emoji_reactions_by + ) + plug( OAuthScopesPlug, %{scopes: ["write:statuses"]} diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index c81e8535e..22da6c0ad 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -13,7 +13,11 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.StatusView - plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles) + plug( + OAuthScopesPlug, + %{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :user_scrobbles + ) + plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles) def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 04c1c5941..db158d366 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -312,14 +312,10 @@ defmodule Pleroma.Web.Router do post("/scrobble", ScrobbleController, :new_scrobble) end - scope [] do - pipe_through(:api) - get("/accounts/:id/favourites", AccountController, :favourites) - end - scope [] do pipe_through(:authenticated_api) + get("/accounts/:id/favourites", AccountController, :favourites) post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) end @@ -353,6 +349,8 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) + get("/apps/verify_credentials", AppController, :verify_credentials) + get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) @@ -431,6 +429,7 @@ defmodule Pleroma.Web.Router do get("/timelines/home", TimelineController, :home) get("/timelines/direct", TimelineController, :direct) + get("/timelines/list/:list_id", TimelineController, :list) end scope "/api/web", Pleroma.Web do @@ -442,15 +441,24 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:api) - post("/accounts", AccountController, :create) get("/accounts/search", SearchController, :account_search) + get("/search", SearchController, :search) + + get("/accounts/:id/statuses", AccountController, :statuses) + get("/accounts/:id/followers", AccountController, :followers) + get("/accounts/:id/following", AccountController, :following) + get("/accounts/:id", AccountController, :show) + + post("/accounts", AccountController, :create) get("/instance", InstanceController, :show) get("/instance/peers", InstanceController, :peers) post("/apps", AppController, :create) - get("/apps/verify_credentials", AppController, :verify_credentials) + get("/statuses", StatusController, :index) + get("/statuses/:id", StatusController, :show) + get("/statuses/:id/context", StatusController, :context) get("/statuses/:id/card", StatusController, :card) get("/statuses/:id/favourited_by", StatusController, :favourited_by) get("/statuses/:id/reblogged_by", StatusController, :reblogged_by) @@ -461,20 +469,8 @@ defmodule Pleroma.Web.Router do get("/timelines/public", TimelineController, :public) get("/timelines/tag/:tag", TimelineController, :hashtag) - get("/timelines/list/:list_id", TimelineController, :list) - - get("/statuses", StatusController, :index) - get("/statuses/:id", StatusController, :show) - get("/statuses/:id/context", StatusController, :context) get("/polls/:id", PollController, :show) - - get("/accounts/:id/statuses", AccountController, :statuses) - get("/accounts/:id/followers", AccountController, :followers) - get("/accounts/:id/following", AccountController, :following) - get("/accounts/:id", AccountController, :show) - - get("/search", SearchController, :search) end scope "/api/v2", Pleroma.Web.MastodonAPI do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 55228616a..e4f182b02 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read ) - plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token]) + plug(:skip_plug, OAuthScopesPlug when action in [:confirm_email, :oauth_tokens, :revoke_token]) action_fallback(:errors) @@ -47,13 +47,13 @@ defmodule Pleroma.Web.TwitterAPI.Controller do json_reply(conn, 201, "") end - def errors(conn, {:param_cast, _}) do + defp errors(conn, {:param_cast, _}) do conn |> put_status(400) |> json("Invalid parameters") end - def errors(conn, _) do + defp errors(conn, _) do conn |> put_status(500) |> json("Something went wrong") diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index ec04c05f0..e2416fb2e 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -67,13 +67,25 @@ defmodule Pleroma.Web do # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do - with %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn), + with %Plug.Conn{halted: false} <- maybe_drop_authentication_if_oauth_check_ignored(conn), + %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn), %Plug.Conn{halted: false} <- maybe_perform_authenticated_check(conn), %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do super(conn, params) end end + # For non-authenticated API actions, drops auth info if OAuth scopes check was ignored + # (neither performed nor explicitly skipped) + defp maybe_drop_authentication_if_oauth_check_ignored(conn) do + if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) and + not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do + OAuthScopesPlug.drop_auth_info(conn) + else + conn + end + end + # Ensures instance is public -or- user is authenticated if such check was scheduled defp maybe_perform_public_or_authenticated_check(conn) do if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do -- cgit v1.2.3 From 1b06a27746ccbbdec77b7bc1571783a64ade4431 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 22 Apr 2020 20:20:19 +0400 Subject: Update Flake ID description --- lib/pleroma/web/api_spec/schemas/flake_id.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/flake_id.ex b/lib/pleroma/web/api_spec/schemas/flake_id.ex index b8e03b8a1..3b5f6477a 100644 --- a/lib/pleroma/web/api_spec/schemas/flake_id.ex +++ b/lib/pleroma/web/api_spec/schemas/flake_id.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do OpenApiSpex.schema(%{ title: "FlakeID", description: - "Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings", + "Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings", type: :string }) end -- cgit v1.2.3 From e62173dfc8b739508345b7ab97477ae04fcdb457 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 18:40:53 +0200 Subject: SideEffects: Run in transaction. This fixes race conditions. --- lib/pleroma/web/activity_pub/side_effects.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 6a8f1af96..a0f71fd88 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -15,12 +15,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Add like to object # - Set up notification def handle(%{data: %{"type" => "Like"}} = object, meta) do - liked_object = Object.get_by_ap_id(object.data["object"]) - Utils.add_like_to_object(object, liked_object) + Pleroma.Repo.transaction(fn -> + liked_object = Object.get_by_ap_id(object.data["object"]) + Utils.add_like_to_object(object, liked_object) - Notification.create_notifications(object) + Notification.create_notifications(object) - {:ok, object, meta} + {:ok, object, meta} + end) + |> (fn {:ok, res} -> res end).() end # Nothing to do -- cgit v1.2.3 From f5bda09de648a6de3151c8614005ebc70447facb Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 19:02:22 +0200 Subject: Stats: Use `invisible` property for filtering. --- lib/pleroma/stats.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 6763786a7..8d2809bbb 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -82,8 +82,7 @@ defmodule Pleroma.Stats do where: u.deactivated != true, where: u.local == true, where: not is_nil(u.nickname), - where: fragment("? not like 'internal.%'", u.nickname), - where: fragment("? not like '%/relay'", u.ap_id) + where: not u.invisible ) user_count = Repo.aggregate(users_query, :count, :id) -- cgit v1.2.3 From 1bcbdc7a9f5df35f42d1ab331bc4b6785e180284 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 22 Apr 2020 21:21:21 +0200 Subject: SideEffects: Use less cryptic syntax. --- lib/pleroma/web/activity_pub/side_effects.ex | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index a0f71fd88..5981e7545 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -15,15 +15,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Add like to object # - Set up notification def handle(%{data: %{"type" => "Like"}} = object, meta) do - Pleroma.Repo.transaction(fn -> - liked_object = Object.get_by_ap_id(object.data["object"]) - Utils.add_like_to_object(object, liked_object) + {:ok, result} = + Pleroma.Repo.transaction(fn -> + liked_object = Object.get_by_ap_id(object.data["object"]) + Utils.add_like_to_object(object, liked_object) - Notification.create_notifications(object) + Notification.create_notifications(object) - {:ok, object, meta} - end) - |> (fn {:ok, res} -> res end).() + {:ok, object, meta} + end) + + result end # Nothing to do -- cgit v1.2.3 From 7d38197894692306c940b55045b91d563e138284 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 23 Apr 2020 13:33:30 +0200 Subject: CommonAPI: Don't make repeating announces possible --- lib/pleroma/web/common_api/common_api.ex | 23 +++++++++++++---------- lib/pleroma/web/common_api/utils.ex | 18 ------------------ 2 files changed, 13 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index f50a909aa..d1efe0c36 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -86,8 +86,9 @@ defmodule Pleroma.Web.CommonAPI do end end - def repeat(id_or_ap_id, user, params \\ %{}) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)}, + def repeat(id, user, params \\ %{}) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)}, object <- Object.normalize(activity), announce_activity <- Utils.get_existing_announce(user.ap_id, object), public <- public_announce?(object, params) do @@ -102,8 +103,9 @@ defmodule Pleroma.Web.CommonAPI do end end - def unrepeat(id_or_ap_id, user) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do + def unrepeat(id, user) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)} do object = Object.normalize(activity) ActivityPub.unannounce(user, object) else @@ -160,8 +162,9 @@ defmodule Pleroma.Web.CommonAPI do end end - def unfavorite(id_or_ap_id, user) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do + def unfavorite(id, user) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)} do object = Object.normalize(activity) ActivityPub.unlike(user, object) else @@ -332,12 +335,12 @@ defmodule Pleroma.Web.CommonAPI do defp maybe_create_activity_expiration(result, _), do: result - def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do + def pin(id, %{ap_id: user_ap_id} = user) do with %Activity{ actor: ^user_ap_id, data: %{"type" => "Create"}, object: %Object{data: %{"type" => object_type}} - } = activity <- get_by_id_or_ap_id(id_or_ap_id), + } = activity <- Activity.get_by_id_with_object(id), true <- object_type in ["Note", "Article", "Question"], true <- Visibility.is_public?(activity), {:ok, _user} <- User.add_pinnned_activity(user, activity) do @@ -348,8 +351,8 @@ defmodule Pleroma.Web.CommonAPI do end end - def unpin(id_or_ap_id, user) do - with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), + def unpin(id, user) do + with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), {:ok, _user} <- User.remove_pinnned_activity(user, activity) do {:ok, activity} else diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 7eec5aa09..945e63e22 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -22,24 +22,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do require Logger require Pleroma.Constants - # This is a hack for twidere. - def get_by_id_or_ap_id(id) do - activity = - with true <- FlakeId.flake_id?(id), - %Activity{} = activity <- Activity.get_by_id_with_object(id) do - activity - else - _ -> Activity.get_create_by_object_ap_id_with_object(id) - end - - activity && - if activity.data["type"] == "Create" do - activity - else - Activity.get_create_by_object_ap_id_with_object(activity.data["object"]) - end - end - def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do attachments_from_ids_descs(ids, desc) end -- cgit v1.2.3 From 89f38d94c754d33736e2444859d6020b2147eaf7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 23 Apr 2020 21:47:33 +0300 Subject: [#2409] Fixed before-action callback results persistence. --- lib/pleroma/web/web.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index e2416fb2e..08e42a7e5 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -67,10 +67,10 @@ defmodule Pleroma.Web do # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do - with %Plug.Conn{halted: false} <- maybe_drop_authentication_if_oauth_check_ignored(conn), - %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn), - %Plug.Conn{halted: false} <- maybe_perform_authenticated_check(conn), - %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do + with %{halted: false} = conn <- maybe_drop_authentication_if_oauth_check_ignored(conn), + %{halted: false} = conn <- maybe_perform_public_or_authenticated_check(conn), + %{halted: false} = conn <- maybe_perform_authenticated_check(conn), + %{halted: false} = conn <- maybe_halt_on_missing_oauth_scopes_check(conn) do super(conn, params) end end -- cgit v1.2.3 From f362836742aabd5b60b92c1296f2bbb6d83a3d59 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 24 Apr 2020 14:46:59 +0400 Subject: Support validation for inline OpenAPI schema and automatic tests for examples --- .../web/api_spec/operations/app_operation.ex | 60 ++++++++++++++++++++-- .../api_spec/operations/custom_emoji_operation.ex | 40 ++++++++++++++- .../api_spec/operations/domain_block_operation.ex | 31 +++++++++-- .../web/api_spec/schemas/app_create_request.ex | 33 ------------ .../web/api_spec/schemas/app_create_response.ex | 33 ------------ .../web/api_spec/schemas/custom_emojis_response.ex | 42 --------------- .../web/api_spec/schemas/domain_block_request.ex | 20 -------- .../web/api_spec/schemas/domain_blocks_response.ex | 16 ------ lib/pleroma/web/oauth/scopes.ex | 6 +-- 9 files changed, 121 insertions(+), 160 deletions(-) delete mode 100644 lib/pleroma/web/api_spec/schemas/app_create_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/app_create_response.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/domain_block_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index 26d8dbd42..035ef2470 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -6,8 +6,6 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers - alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest - alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -22,9 +20,9 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do summary: "Create an application", description: "Create a new application to obtain OAuth2 credentials", operationId: "AppController.create", - requestBody: Helpers.request_body("Parameters", AppCreateRequest, required: true), + requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("App", "application/json", AppCreateResponse), + 200 => Operation.response("App", "application/json", create_response()), 422 => Operation.response( "Unprocessable Entity", @@ -93,4 +91,58 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do } } end + + defp create_request do + %Schema{ + title: "AppCreateRequest", + description: "POST body for creating an app", + type: :object, + properties: %{ + client_name: %Schema{type: :string, description: "A name for your application."}, + redirect_uris: %Schema{ + type: :string, + description: + "Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter." + }, + scopes: %Schema{ + type: :string, + description: "Space separated list of scopes", + default: "read" + }, + website: %Schema{type: :string, description: "A URL to the homepage of your app"} + }, + required: [:client_name, :redirect_uris], + example: %{ + "client_name" => "My App", + "redirect_uris" => "https://myapp.com/auth/callback", + "website" => "https://myapp.com/" + } + } + 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/" + } + } + end end diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex index cf2215823..a117fe460 100644 --- a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex +++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex @@ -4,7 +4,8 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do alias OpenApiSpex.Operation - alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -18,8 +19,43 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do description: "Returns custom emojis that are available on the server.", operationId: "CustomEmojiController.index", responses: %{ - 200 => Operation.response("Custom Emojis", "application/json", CustomEmojisResponse) + 200 => Operation.response("Custom Emojis", "application/json", custom_emojis_resposnse()) } } end + + defp custom_emojis_resposnse do + %Schema{ + title: "CustomEmojisResponse", + description: "Response schema for custom emojis", + type: :array, + items: CustomEmoji, + example: [ + %{ + "category" => "Fun", + "shortcode" => "blank", + "static_url" => "https://lain.com/emoji/blank.png", + "tags" => ["Fun"], + "url" => "https://lain.com/emoji/blank.png", + "visible_in_picker" => false + }, + %{ + "category" => "Gif,Fun", + "shortcode" => "firefox", + "static_url" => "https://lain.com/emoji/Firefox.gif", + "tags" => ["Gif", "Fun"], + "url" => "https://lain.com/emoji/Firefox.gif", + "visible_in_picker" => true + }, + %{ + "category" => "pack:mixed", + "shortcode" => "sadcat", + "static_url" => "https://lain.com/emoji/mixed/sadcat.png", + "tags" => ["pack:mixed"], + "url" => "https://lain.com/emoji/mixed/sadcat.png", + "visible_in_picker" => true + } + ] + } + end end diff --git a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex index dd14837c3..3b7f51ceb 100644 --- a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex +++ b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex @@ -6,8 +6,6 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers - alias Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest - alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -22,7 +20,13 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do security: [%{"oAuth" => ["follow", "read:blocks"]}], operationId: "DomainBlockController.index", responses: %{ - 200 => Operation.response("Domain blocks", "application/json", DomainBlocksResponse) + 200 => + Operation.response("Domain blocks", "application/json", %Schema{ + description: "Response schema for domain blocks", + type: :array, + items: %Schema{type: :string}, + example: ["google.com", "facebook.com"] + }) } } end @@ -40,7 +44,7 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do - prevent following new users from it (but does not remove existing follows) """, operationId: "DomainBlockController.create", - requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true), + requestBody: domain_block_request(), security: [%{"oAuth" => ["follow", "write:blocks"]}], responses: %{ 200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) @@ -54,11 +58,28 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do summary: "Unblock a domain", description: "Remove a domain block, if it exists in the user's array of blocked domains.", operationId: "DomainBlockController.delete", - requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true), + requestBody: domain_block_request(), security: [%{"oAuth" => ["follow", "write:blocks"]}], responses: %{ 200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) } } end + + defp domain_block_request do + Helpers.request_body( + "Parameters", + %Schema{ + type: :object, + properties: %{ + domain: %Schema{type: :string} + }, + required: [:domain] + }, + required: true, + example: %{ + "domain" => "facebook.com" + } + ) + end end diff --git a/lib/pleroma/web/api_spec/schemas/app_create_request.ex b/lib/pleroma/web/api_spec/schemas/app_create_request.ex deleted file mode 100644 index 8a83abef3..000000000 --- a/lib/pleroma/web/api_spec/schemas/app_create_request.ex +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateRequest do - alias OpenApiSpex.Schema - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AppCreateRequest", - description: "POST body for creating an app", - type: :object, - properties: %{ - client_name: %Schema{type: :string, description: "A name for your application."}, - redirect_uris: %Schema{ - type: :string, - description: - "Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter." - }, - scopes: %Schema{ - type: :string, - description: "Space separated list of scopes. If none is provided, defaults to `read`." - }, - website: %Schema{type: :string, description: "A URL to the homepage of your app"} - }, - required: [:client_name, :redirect_uris], - example: %{ - "client_name" => "My App", - "redirect_uris" => "https://myapp.com/auth/callback", - "website" => "https://myapp.com/" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/app_create_response.ex b/lib/pleroma/web/api_spec/schemas/app_create_response.ex deleted file mode 100644 index f290fb031..000000000 --- a/lib/pleroma/web/api_spec/schemas/app_create_response.ex +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateResponse do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.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/" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex b/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex deleted file mode 100644 index 01582a63d..000000000 --- a/lib/pleroma/web/api_spec/schemas/custom_emojis_response.ex +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse do - alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "CustomEmojisResponse", - description: "Response schema for custom emojis", - type: :array, - items: CustomEmoji, - example: [ - %{ - "category" => "Fun", - "shortcode" => "blank", - "static_url" => "https://lain.com/emoji/blank.png", - "tags" => ["Fun"], - "url" => "https://lain.com/emoji/blank.png", - "visible_in_picker" => true - }, - %{ - "category" => "Gif,Fun", - "shortcode" => "firefox", - "static_url" => "https://lain.com/emoji/Firefox.gif", - "tags" => ["Gif", "Fun"], - "url" => "https://lain.com/emoji/Firefox.gif", - "visible_in_picker" => true - }, - %{ - "category" => "pack:mixed", - "shortcode" => "sadcat", - "static_url" => "https://lain.com/emoji/mixed/sadcat.png", - "tags" => ["pack:mixed"], - "url" => "https://lain.com/emoji/mixed/sadcat.png", - "visible_in_picker" => true - } - ] - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/domain_block_request.ex b/lib/pleroma/web/api_spec/schemas/domain_block_request.ex deleted file mode 100644 index ee9238361..000000000 --- a/lib/pleroma/web/api_spec/schemas/domain_block_request.ex +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest do - alias OpenApiSpex.Schema - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "DomainBlockRequest", - type: :object, - properties: %{ - domain: %Schema{type: :string} - }, - required: [:domain], - example: %{ - "domain" => "facebook.com" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex b/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex deleted file mode 100644 index d895aca4e..000000000 --- a/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex +++ /dev/null @@ -1,16 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse do - require OpenApiSpex - alias OpenApiSpex.Schema - - OpenApiSpex.schema(%{ - title: "DomainBlocksResponse", - description: "Response schema for domain blocks", - type: :array, - items: %Schema{type: :string}, - example: ["google.com", "facebook.com"] - }) -end diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex index 1023f16d4..6f06f1431 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -17,12 +17,8 @@ defmodule Pleroma.Web.OAuth.Scopes do """ @spec fetch_scopes(map() | struct(), list()) :: list() - def fetch_scopes(%Pleroma.Web.ApiSpec.Schemas.AppCreateRequest{scopes: scopes}, default) do - parse_scopes(scopes, default) - end - def fetch_scopes(params, default) do - parse_scopes(params["scope"] || params["scopes"], default) + parse_scopes(params["scope"] || params["scopes"] || params[:scopes], default) end def parse_scopes(scopes, _default) when is_list(scopes) do -- cgit v1.2.3 From bbf8554c975ea1ba9b5c809a7891ec0fb4a8e537 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 24 Apr 2020 13:48:13 +0200 Subject: ActivitPub: Remove `like` function. We don't need another way to build likes. --- lib/pleroma/web/activity_pub/activity_pub.ex | 30 ---------------------- .../web/activity_pub/activity_pub_controller.ex | 7 ++++- 2 files changed, 6 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 4a133498e..c67b3335d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -398,36 +398,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - # TODO: This is weird, maybe we shouldn't check here if we can make the activity. - @spec like(User.t(), Object.t(), String.t() | nil, boolean()) :: - {:ok, Activity.t(), Object.t()} | {:error, any()} - def like(user, object, activity_id \\ nil, local \\ true) do - with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do - result - end - end - - defp do_like( - %User{ap_id: ap_id} = user, - %Object{data: %{"id" => _}} = object, - activity_id, - local - ) do - with nil <- get_existing_like(ap_id, object), - like_data <- make_like_data(user, object, activity_id), - {:ok, activity} <- insert(like_data, local), - {:ok, object} <- add_like_to_object(activity, object), - :ok <- maybe_federate(activity) do - {:ok, activity, object} - else - %Activity{} = activity -> - {:ok, activity, object} - - {:error, error} -> - Repo.rollback(error) - end - end - @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) :: {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()} def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 8b9eb4a2c..325a714b4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.Plugs.EnsureAuthenticatedPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Relay @@ -421,7 +423,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do with %Object{} = object <- Object.normalize(params["object"]), - {:ok, activity, _object} <- ActivityPub.like(user, object) do + {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)}, + {_, {:ok, %Activity{} = activity, _meta}} <- + {:common_pipeline, + Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do {:ok, activity} else _ -> {:error, dgettext("errors", "Can't like object")} -- cgit v1.2.3 From 1df6af2a4c93257f94e2780e4317cfcf9bef7adb Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 24 Apr 2020 13:59:48 +0200 Subject: Credo fixes. --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 325a714b4..d625530ec 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -13,9 +13,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.ObjectView + alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.UserView -- cgit v1.2.3 From cb12585098e0cc1e2e85d253812e1898e8034b7f Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 24 Apr 2020 14:37:53 +0200 Subject: Announcements: Prevent race condition. --- lib/pleroma/web/activity_pub/activity_pub.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c67b3335d..4cce4f13c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -438,6 +438,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp do_announce(user, object, activity_id, local, public) do with true <- is_announceable?(object, user, public), + object <- Object.get_by_id(object.id), announce_data <- make_announce_data(user, object, activity_id, public), {:ok, activity} <- insert(announce_data, local), {:ok, object} <- add_announce_to_object(activity, object), -- cgit v1.2.3 From 6e625a427cdc829714ad0365560d79aa4ee9c2e5 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 4 Dec 2019 09:49:17 +0300 Subject: reply filtering --- lib/pleroma/user.ex | 14 ++++ lib/pleroma/user/query.ex | 4 +- lib/pleroma/web/activity_pub/activity_pub.ex | 75 +++++++++++++++++++--- lib/pleroma/web/common_api/activity_draft.ex | 16 +++-- .../controllers/timeline_controller.ex | 1 + 5 files changed, 94 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 477237756..b451202b2 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -832,6 +832,7 @@ defmodule Pleroma.User do def set_cache(%User{} = user) do Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) Cachex.put(:user_cache, "nickname:#{user.nickname}", user) + Cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user)) {:ok, user} end @@ -847,9 +848,22 @@ defmodule Pleroma.User do end end + def get_user_friends_ap_ids(user) do + from(u in User.get_friends_query(user), select: u.ap_id) + |> Repo.all() + end + + @spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()] + def get_cached_user_friends_ap_ids(user) do + Cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ -> + get_user_friends_ap_ids(user) + end) + end + def invalidate_cache(user) do Cachex.del(:user_cache, "ap_id:#{user.ap_id}") Cachex.del(:user_cache, "nickname:#{user.nickname}") + Cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}") end @spec get_cached_by_ap_id(String.t()) :: User.t() | nil diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index ec88088cf..ac77aab71 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -54,13 +54,13 @@ defmodule Pleroma.User.Query do select: term(), limit: pos_integer() } - | %{} + | map() @ilike_criteria [:nickname, :name, :query] @equal_criteria [:email] @contains_criteria [:ap_id, :nickname] - @spec build(criteria()) :: Query.t() + @spec build(Query.t(), criteria()) :: Query.t() def build(query \\ base_query(), criteria) do prepare_query(query, criteria) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c67b3335d..8b170b7f8 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -270,9 +270,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ), {:ok, activity} <- insert(create_data, local, fake), {:fake, false, activity} <- {:fake, fake, activity}, + {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, _ <- increase_replies_count_if_reply(create_data), _ <- increase_poll_votes_if_vote(create_data), - {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} @@ -700,12 +700,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do do: [opts["user"].ap_id | User.following(opts["user"])] ++ public, else: public + opts = Map.put(opts, "user", opts["user"]) + from(activity in Activity) |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> restrict_blocked(opts) - |> restrict_recipients(recipients, opts["user"]) + |> restrict_recipients(recipients, opts) |> where( [activity], fragment( @@ -740,7 +742,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do - opts = Map.drop(opts, ["user"]) + opts = + opts + |> Map.put("reply_user", opts["user"]) + |> Map.delete("user") [Constants.as_public()] |> fetch_activities_query(opts) @@ -976,13 +981,65 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_tag(query, _), do: query - defp restrict_recipients(query, [], _user), do: query + defp reply_recipients(user, "following") do + [user.ap_id | User.get_cached_user_friends_ap_ids(user)] + end + + defp reply_recipients(user, "self"), do: [user.ap_id] + + defp restrict_recipients(query, [], _opts), do: query + + defp restrict_recipients( + query, + recipients, + %{"user" => nil, "reply_user" => user, "reply_visibility" => visibility} + ) + when not is_nil(user) and visibility in ["following", "self"] do + reply_recipients = reply_recipients(user, visibility) - defp restrict_recipients(query, recipients, nil) do - from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) + from([activity, object] in query, + where: + fragment( + "? && ? AND (?->>'inReplyTo' IS NULL OR array_remove(?, ?) && ? OR ? = ?)", + ^recipients, + activity.recipients, + object.data, + activity.recipients, + activity.actor, + ^reply_recipients, + activity.actor, + ^user.ap_id + ) + ) end - defp restrict_recipients(query, recipients, user) do + defp restrict_recipients(query, recipients, %{"user" => nil}) do + from(activity in query, + where: fragment("? && ?", ^recipients, activity.recipients) + ) + end + + defp restrict_recipients(query, recipients, %{"user" => user, "reply_visibility" => visibility}) + when visibility in ["following", "self"] do + reply_recipients = reply_recipients(user, visibility) + + from( + [activity, object] in query, + where: + fragment( + "? && ? AND (?->>'inReplyTo' IS NULL OR array_remove(?, ?) && ?)", + ^recipients, + activity.recipients, + object.data, + activity.recipients, + activity.actor, + ^reply_recipients + ), + or_where: activity.actor == ^user.ap_id + ) + end + + defp restrict_recipients(query, recipients, %{"user" => user}) do from( activity in query, where: fragment("? && ?", ^recipients, activity.recipients), @@ -1254,13 +1311,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do skip_thread_containment: Config.get([:instance, :skip_thread_containment]) } + opts = Map.put(opts, "user", opts["user"]) + Activity |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_preload_report_notes(opts) |> maybe_set_thread_muted_field(opts) |> maybe_order(opts) - |> restrict_recipients(recipients, opts["user"]) + |> restrict_recipients(recipients, opts) |> restrict_tag(opts) |> restrict_tag_reject(opts) |> restrict_tag_all(opts) diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index c1cd15bb2..244cf2be5 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -84,14 +84,18 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | attachments: attachments} end - defp in_reply_to(draft) do - case Map.get(draft.params, "in_reply_to_status_id") do - "" -> draft - nil -> draft - id -> %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)} - end + defp in_reply_to(%{params: %{"in_reply_to_status_id" => ""}} = draft), do: draft + + defp in_reply_to(%{params: %{"in_reply_to_status_id" => id}} = draft) when is_binary(id) do + %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)} end + defp in_reply_to(%{params: %{"in_reply_to_status_id" => %Activity{} = in_reply_to}} = draft) do + %__MODULE__{draft | in_reply_to: in_reply_to} + end + + defp in_reply_to(draft), do: draft + defp in_reply_to_conversation(draft) do in_reply_to_conversation = Participation.get(draft.params["in_reply_to_conversation_id"]) %__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation} diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index b3c58005e..a2ac9301e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -100,6 +100,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) |> Map.put("muting_user", user) + |> Map.put("user", user) |> ActivityPub.fetch_public_activities() conn -- cgit v1.2.3 From 00e62161f64802317d7d789e7eac42c33f0540f5 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 24 Apr 2020 16:52:38 +0300 Subject: [#2409] Tested all auth setup configs in AuthTestControllerTest. Adjusted :skip_plug definitions for some endpoints. --- lib/pleroma/tests/auth_test_controller.ex | 93 ++++++++++++++++++++++ lib/pleroma/tests/oauth_test_controller.ex | 31 -------- lib/pleroma/web/masto_fe_controller.ex | 5 +- .../mastodon_api/controllers/account_controller.ex | 13 ++- .../mastodon_api/controllers/status_controller.ex | 4 +- .../controllers/timeline_controller.ex | 4 +- lib/pleroma/web/oauth/oauth_controller.ex | 5 +- .../pleroma_api/controllers/account_controller.ex | 5 +- lib/pleroma/web/router.ex | 21 ++++- .../web/twitter_api/twitter_api_controller.ex | 8 +- 10 files changed, 137 insertions(+), 52 deletions(-) create mode 100644 lib/pleroma/tests/auth_test_controller.ex delete mode 100644 lib/pleroma/tests/oauth_test_controller.ex (limited to 'lib') diff --git a/lib/pleroma/tests/auth_test_controller.ex b/lib/pleroma/tests/auth_test_controller.ex new file mode 100644 index 000000000..fb04411d9 --- /dev/null +++ b/lib/pleroma/tests/auth_test_controller.ex @@ -0,0 +1,93 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +# A test controller reachable only in :test env. +defmodule Pleroma.Tests.AuthTestController do + @moduledoc false + + use Pleroma.Web, :controller + + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.User + + # Serves only with proper OAuth token (:api and :authenticated_api) + # Skipping EnsurePublicOrAuthenticatedPlug has no effect in this case + # + # Suggested use case: all :authenticated_api endpoints (makes no sense for :api endpoints) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :do_oauth_check) + + # Via :api, keeps :user if token has requested scopes (if :user is dropped, serves if public) + # Via :authenticated_api, serves if token is present and has requested scopes + # + # Suggested use case: vast majority of :api endpoints (no sense for :authenticated_api ones) + plug( + OAuthScopesPlug, + %{scopes: ["read"], fallback: :proceed_unauthenticated} + when action == :fallback_oauth_check + ) + + # Keeps :user if present, executes regardless of token / token scopes + # Fails with no :user for :authenticated_api / no user for :api on private instance + # Note: EnsurePublicOrAuthenticatedPlug is not skipped (private instance fails on no :user) + # Note: Basic Auth processing results in :skip_plug call for OAuthScopesPlug + # + # Suggested use: suppressing OAuth checks for other auth mechanisms (like Basic Auth) + # For controller-level use, see :skip_oauth_skip_publicity_check instead + plug( + :skip_plug, + OAuthScopesPlug when action == :skip_oauth_check + ) + + # (Shouldn't be executed since the plug is skipped) + plug(OAuthScopesPlug, %{scopes: ["admin"]} when action == :skip_oauth_check) + + # Via :api, keeps :user if token has requested scopes, and continues with nil :user otherwise + # Via :authenticated_api, serves if token is present and has requested scopes + # + # Suggested use: as :fallback_oauth_check but open with nil :user for :api on private instances + plug( + :skip_plug, + EnsurePublicOrAuthenticatedPlug when action == :fallback_oauth_skip_publicity_check + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read"], fallback: :proceed_unauthenticated} + when action == :fallback_oauth_skip_publicity_check + ) + + # Via :api, keeps :user if present, serves regardless of token presence / scopes / :user presence + # Via :authenticated_api, serves if :user is set (regardless of token presence and its scopes) + # + # Suggested use: making an :api endpoint always accessible (e.g. email confirmation endpoint) + plug( + :skip_plug, + [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] + when action == :skip_oauth_skip_publicity_check + ) + + # Via :authenticated_api, always fails with 403 (endpoint is insecure) + # Via :api, drops :user if present and serves if public (private instance rejects on no user) + # + # Suggested use: none; please define OAuth rules for all :api / :authenticated_api endpoints + plug(:skip_plug, [] when action == :missing_oauth_check_definition) + + def do_oauth_check(conn, _params), do: conn_state(conn) + + def fallback_oauth_check(conn, _params), do: conn_state(conn) + + def skip_oauth_check(conn, _params), do: conn_state(conn) + + def fallback_oauth_skip_publicity_check(conn, _params), do: conn_state(conn) + + def skip_oauth_skip_publicity_check(conn, _params), do: conn_state(conn) + + def missing_oauth_check_definition(conn, _params), do: conn_state(conn) + + defp conn_state(%{assigns: %{user: %User{} = user}} = conn), + do: json(conn, %{user_id: user.id}) + + defp conn_state(conn), do: json(conn, %{user_id: nil}) +end diff --git a/lib/pleroma/tests/oauth_test_controller.ex b/lib/pleroma/tests/oauth_test_controller.ex deleted file mode 100644 index 58d517f78..000000000 --- a/lib/pleroma/tests/oauth_test_controller.ex +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -# A test controller reachable only in :test env. -# Serves to test OAuth scopes check skipping / enforcement. -defmodule Pleroma.Tests.OAuthTestController do - @moduledoc false - - use Pleroma.Web, :controller - - alias Pleroma.Plugs.OAuthScopesPlug - - plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth) - - plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth) - - def skipped_oauth(conn, _params) do - noop(conn) - end - - def performed_oauth(conn, _params) do - noop(conn) - end - - def missed_oauth(conn, _params) do - noop(conn) - end - - defp noop(conn), do: json(conn, %{}) -end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 9a2ec517a..d0d8bc8eb 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -5,12 +5,15 @@ defmodule Pleroma.Web.MastoFEController do use Pleroma.Web, :controller + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) # Note: :index action handles attempt of unauthenticated access to private instance with redirect + plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action == :index) + plug( OAuthScopesPlug, %{scopes: ["read"], fallback: :proceed_unauthenticated} @@ -19,7 +22,7 @@ defmodule Pleroma.Web.MastoFEController do plug( :skip_plug, - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :manifest] + [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :manifest ) @doc "GET /web/*path" diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index e3465e659..f39825e08 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do skip_relationships?: 1 ] + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.User @@ -26,18 +27,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI - plug(:skip_plug, OAuthScopesPlug when action in [:create, :identity_proofs]) + plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create) - plug( - :skip_plug, - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - when action in [:create, :show, :statuses] - ) + plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:show, :statuses]) plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} - when action in [:show, :followers, :following, :endorsements] + when action in [:show, :followers, :following] ) plug( @@ -49,7 +46,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, %{scopes: ["read:accounts"]} - when action in [:endorsements, :verify_credentials] + when action in [:verify_credentials, :endorsements, :identity_proofs] ) plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index eade83aaf..4fa9a2120 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -24,6 +24,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView + plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]) + @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} plug( @@ -77,8 +79,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do %{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark] ) - plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]) - @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a plug( diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 040a0b9dd..fb6b18ed5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag]) + # TODO: Replace with a macro when there is a Phoenix release with the following commit in it: # https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e @@ -33,8 +35,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do when action in [:public, :hashtag] ) - plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag]) - plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) # GET /api/v1/timelines/home diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 0121cd661..685269877 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -25,9 +25,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_session) plug(:fetch_flash) - plug(RateLimiter, [name: :authentication] when action == :create_authorization) - plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug) + plug(:skip_plug, [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]) + + plug(RateLimiter, [name: :authentication] when action == :create_authorization) action_fallback(Pleroma.Web.OAuth.FallbackController) diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index d6ffdcbe4..237c8157e 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1] alias Ecto.Changeset + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.User @@ -17,11 +18,9 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do require Pleroma.Constants - plug(:skip_plug, OAuthScopesPlug when action == :confirmation_resend) - plug( :skip_plug, - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :confirmation_resend + [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirmation_resend ) plug( diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index db158d366..57efc3314 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -655,11 +655,28 @@ defmodule Pleroma.Web.Router do # Test-only routes needed to test action dispatching and plug chain execution if Pleroma.Config.get(:env) == :test do + @test_actions [ + :do_oauth_check, + :fallback_oauth_check, + :skip_oauth_check, + :fallback_oauth_skip_publicity_check, + :skip_oauth_skip_publicity_check, + :missing_oauth_check_definition + ] + + scope "/test/api", Pleroma.Tests do + pipe_through(:api) + + for action <- @test_actions do + get("/#{action}", AuthTestController, action) + end + end + scope "/test/authenticated_api", Pleroma.Tests do pipe_through(:authenticated_api) - for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do - get("/#{action}", OAuthTestController, action) + for action <- @test_actions do + get("/#{action}", AuthTestController, action) end end end diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index e4f182b02..c2de26b0b 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller alias Pleroma.Notification + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.OAuth.Token @@ -18,7 +19,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read ) - plug(:skip_plug, OAuthScopesPlug when action in [:confirm_email, :oauth_tokens, :revoke_token]) + plug( + :skip_plug, + [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email + ) + + plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token]) action_fallback(:errors) -- cgit v1.2.3 From d89cd0a19733eec27b79b768df2e30a68bfc6d6b Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 24 Apr 2020 18:25:26 +0200 Subject: Reply Filtering: Refactor. --- lib/pleroma/web/activity_pub/activity_pub.ex | 112 ++++++++------------- .../controllers/timeline_controller.ex | 3 +- 2 files changed, 46 insertions(+), 69 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8b170b7f8..9ec31fb03 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -270,9 +270,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ), {:ok, activity} <- insert(create_data, local, fake), {:fake, false, activity} <- {:fake, fake, activity}, - {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, _ <- increase_replies_count_if_reply(create_data), _ <- increase_poll_votes_if_vote(create_data), + {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} @@ -700,14 +700,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do do: [opts["user"].ap_id | User.following(opts["user"])] ++ public, else: public - opts = Map.put(opts, "user", opts["user"]) - from(activity in Activity) |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> restrict_blocked(opts) - |> restrict_recipients(recipients, opts) + |> restrict_recipients(recipients, opts["user"]) |> where( [activity], fragment( @@ -742,10 +740,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do - opts = - opts - |> Map.put("reply_user", opts["user"]) - |> Map.delete("user") + opts = Map.drop(opts, ["user"]) [Constants.as_public()] |> fetch_activities_query(opts) @@ -981,65 +976,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_tag(query, _), do: query - defp reply_recipients(user, "following") do - [user.ap_id | User.get_cached_user_friends_ap_ids(user)] - end - - defp reply_recipients(user, "self"), do: [user.ap_id] - - defp restrict_recipients(query, [], _opts), do: query - - defp restrict_recipients( - query, - recipients, - %{"user" => nil, "reply_user" => user, "reply_visibility" => visibility} - ) - when not is_nil(user) and visibility in ["following", "self"] do - reply_recipients = reply_recipients(user, visibility) + defp restrict_recipients(query, [], _user), do: query - from([activity, object] in query, - where: - fragment( - "? && ? AND (?->>'inReplyTo' IS NULL OR array_remove(?, ?) && ? OR ? = ?)", - ^recipients, - activity.recipients, - object.data, - activity.recipients, - activity.actor, - ^reply_recipients, - activity.actor, - ^user.ap_id - ) - ) - end - - defp restrict_recipients(query, recipients, %{"user" => nil}) do - from(activity in query, - where: fragment("? && ?", ^recipients, activity.recipients) - ) + defp restrict_recipients(query, recipients, nil) do + from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) end - defp restrict_recipients(query, recipients, %{"user" => user, "reply_visibility" => visibility}) - when visibility in ["following", "self"] do - reply_recipients = reply_recipients(user, visibility) - - from( - [activity, object] in query, - where: - fragment( - "? && ? AND (?->>'inReplyTo' IS NULL OR array_remove(?, ?) && ?)", - ^recipients, - activity.recipients, - object.data, - activity.recipients, - activity.actor, - ^reply_recipients - ), - or_where: activity.actor == ^user.ap_id - ) - end - - defp restrict_recipients(query, recipients, %{"user" => user}) do + defp restrict_recipients(query, recipients, user) do from( activity in query, where: fragment("? && ?", ^recipients, activity.recipients), @@ -1104,6 +1047,41 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + defp restrict_replies(query, %{ + "reply_filtering_user" => user, + "reply_visibility" => "self" + }) do + from( + [activity, object] in query, + where: + fragment( + "?->>'inReplyTo' is null OR ? = ANY(?)", + object.data, + ^user.ap_id, + activity.recipients + ) + ) + end + + defp restrict_replies(query, %{ + "reply_filtering_user" => user, + "reply_visibility" => "following" + }) do + from( + [activity, object] in query, + where: + fragment( + "?->>'inReplyTo' is null OR ? && array_remove(?, ?) OR ? = ?", + object.data, + ^[user.ap_id | User.get_cached_user_friends_ap_ids(user)], + activity.recipients, + activity.actor, + activity.actor, + ^user.ap_id + ) + ) + end + defp restrict_replies(query, _), do: query defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do @@ -1311,15 +1289,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do skip_thread_containment: Config.get([:instance, :skip_thread_containment]) } - opts = Map.put(opts, "user", opts["user"]) - Activity |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_preload_report_notes(opts) |> maybe_set_thread_muted_field(opts) |> maybe_order(opts) - |> restrict_recipients(recipients, opts) + |> restrict_recipients(recipients, opts["user"]) + |> restrict_replies(opts) |> restrict_tag(opts) |> restrict_tag_reject(opts) |> restrict_tag_all(opts) @@ -1334,7 +1311,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_media(opts) |> restrict_visibility(opts) |> restrict_thread_visibility(opts, config) - |> restrict_replies(opts) |> restrict_reblogs(opts) |> restrict_pinned(opts) |> restrict_muted_reblogs(restrict_muted_reblogs_opts) diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index a2ac9301e..403d500e0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -37,6 +37,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put("type", ["Create", "Announce"]) |> Map.put("blocking_user", user) |> Map.put("muting_user", user) + |> Map.put("reply_filtering_user", user) |> Map.put("user", user) recipients = [user.ap_id | User.following(user)] @@ -100,7 +101,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) |> Map.put("muting_user", user) - |> Map.put("user", user) + |> Map.put("reply_filtering_user", user) |> ActivityPub.fetch_public_activities() conn -- cgit v1.2.3 From b4139cc5472079a34f0256ac9991a0222844d44c Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 24 Apr 2020 22:25:27 +0300 Subject: [#2409] Made `GET /api/v1/accounts/:id/favourites` auth-optional, adjusted tests. --- lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 2 +- lib/pleroma/web/pleroma_api/controllers/account_controller.ex | 5 ++++- lib/pleroma/web/router.ex | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 4fa9a2120..45601ff59 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -357,7 +357,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/favourites" - def favourites(%{assigns: %{user: user}} = conn, params) do + def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do activities = ActivityPub.fetch_favourites( user, diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 237c8157e..be7477867 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -39,7 +39,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do ] ) - plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) + plug( + OAuthScopesPlug, + %{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites + ) plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 57efc3314..becce3098 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -312,10 +312,14 @@ defmodule Pleroma.Web.Router do post("/scrobble", ScrobbleController, :new_scrobble) end + scope [] do + pipe_through(:api) + get("/accounts/:id/favourites", AccountController, :favourites) + end + scope [] do pipe_through(:authenticated_api) - get("/accounts/:id/favourites", AccountController, :favourites) post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) end @@ -404,6 +408,7 @@ defmodule Pleroma.Web.Router do put("/scheduled_statuses/:id", ScheduledActivityController, :update) delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) + # Unlike `GET /api/v1/accounts/:id/favourites`, demands authentication get("/favourites", StatusController, :favourites) get("/bookmarks", StatusController, :bookmarks) -- cgit v1.2.3 From 0d05e1fe397d75d4381d9059bd1c049ab7030085 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sat, 25 Apr 2020 18:24:10 +0300 Subject: [#1706] Prevented error on unresolved activity actors for timeline actions. --- lib/pleroma/web/admin_api/views/status_view.ex | 18 +++--------------- lib/pleroma/web/mastodon_api/views/status_view.ex | 13 ++++++++++--- 2 files changed, 13 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 360ddc22c..3637dee24 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -8,15 +8,16 @@ defmodule Pleroma.Web.AdminAPI.StatusView do require Pleroma.Constants alias Pleroma.User + alias Pleroma.Web.MastodonAPI.StatusView def render("index.json", opts) do safe_render_many(opts.activities, __MODULE__, "show.json", opts) end def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do - user = get_user(activity.data["actor"]) + user = StatusView.get_user(activity.data["actor"]) - Pleroma.Web.MastodonAPI.StatusView.render("show.json", opts) + StatusView.render("show.json", opts) |> Map.merge(%{account: merge_account_views(user)}) end @@ -26,17 +27,4 @@ defmodule Pleroma.Web.AdminAPI.StatusView do end defp merge_account_views(_), do: %{} - - defp get_user(ap_id) do - cond do - user = User.get_cached_by_ap_id(ap_id) -> - user - - user = User.get_by_guessed_nickname(ap_id) -> - user - - true -> - User.error_user(ap_id) - end - end end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index b5850e1ae..b0c53acd9 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -45,7 +45,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) end - defp get_user(ap_id) do + def get_user(ap_id, fake_record_fallback \\ true) do cond do user = User.get_cached_by_ap_id(ap_id) -> user @@ -53,8 +53,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do user = User.get_by_guessed_nickname(ap_id) -> user - true -> + fake_record_fallback -> + # TODO: refactor (fake records is never a good idea) User.error_user(ap_id) + + true -> nil end end @@ -97,7 +100,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do UserRelationship.view_relationships_option(nil, []) true -> - actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"])) + # Note: unresolved users are filtered out + actors = + (activities ++ parent_activities) + |> Enum.map(&get_user(&1.data["actor"], false)) + |> Enum.filter(& &1) UserRelationship.view_relationships_option(reading_user, actors, source_mutes_only: opts[:skip_relationships] -- cgit v1.2.3 From e16437ff191f17b7ec59504d3c38e582ba76eedc Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sat, 25 Apr 2020 18:42:08 +0300 Subject: [#1706] Formatting fix. --- lib/pleroma/web/mastodon_api/views/status_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index b0c53acd9..1d9082c09 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -57,7 +57,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do # TODO: refactor (fake records is never a good idea) User.error_user(ap_id) - true -> nil + true -> + nil end end -- cgit v1.2.3 From 1bd9749a8f31e5f087b0d0ca75b13f4baf461997 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 Apr 2020 00:28:57 -0500 Subject: Let blob: pass CSP --- lib/pleroma/plugs/http_security_plug.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 81e6b4f2a..6462797b6 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -75,7 +75,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do "default-src 'none'", "base-uri 'self'", "frame-ancestors 'none'", - "img-src 'self' data: https:", + "img-src 'self' data: blob: https:", "media-src 'self' https:", "style-src 'self' 'unsafe-inline'", "font-src 'self'", -- cgit v1.2.3 From a626cb682cc8fd6cad91484db064ed22646960af Mon Sep 17 00:00:00 2001 From: fence Date: Mon, 27 Apr 2020 17:55:33 +0200 Subject: secure mongoose auth endpoint --- .../web/mongooseim/mongoose_im_controller.ex | 33 ++++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index 04d823b36..744cf5227 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -26,21 +26,36 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do end def check_password(conn, %{"user" => username, "pass" => password}) do - with %User{password_hash: password_hash} <- - Repo.get_by(User, nickname: username, local: true), - true <- Pbkdf2.checkpw(password, password_hash) do - conn - |> json(true) - else - false -> + user = Repo.get_by(User, nickname: username, local: true) + + case User.account_status(user) do + :deactivated -> conn - |> put_status(:forbidden) + |> put_status(:not_found) |> json(false) - _ -> + :confirmation_pending -> conn |> put_status(:not_found) |> json(false) + + _ -> + with %User{password_hash: password_hash} <- + user, + true <- Pbkdf2.checkpw(password, password_hash) do + conn + |> json(true) + else + false -> + conn + |> put_status(:forbidden) + |> json(false) + + _ -> + conn + |> put_status(:not_found) + |> json(false) + end end end end -- cgit v1.2.3 From 5c7cc109172c84b991fad7eebbdd51e75f0c5382 Mon Sep 17 00:00:00 2001 From: fence Date: Mon, 27 Apr 2020 18:31:00 +0200 Subject: add tests for deactivated users for mongoose auth --- lib/pleroma/web/mongooseim/mongoose_im_controller.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index 744cf5227..c15b4bfb8 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -27,8 +27,13 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do def check_password(conn, %{"user" => username, "pass" => password}) do user = Repo.get_by(User, nickname: username, local: true) + + state = case user do + nil -> nil + _ -> User.account_status(user) + end - case User.account_status(user) do + case state do :deactivated -> conn |> put_status(:not_found) -- cgit v1.2.3 From 2efc00b3cf5413ae7f8e8411ee1372343ee2618a Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 27 Apr 2020 20:46:52 +0400 Subject: Use `json_response_and_validate_schema/2` in tests to validate OpenAPI schema --- .../web/api_spec/operations/account_operation.ex | 27 ++++++++++++++++------ lib/pleroma/web/api_spec/schemas/account.ex | 2 +- lib/pleroma/web/controller_helper.ex | 5 ++-- .../mastodon_api/controllers/account_controller.ex | 4 ++-- 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index fcf030037..bf8d21059 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Reference alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.Account + alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse alias Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest @@ -38,7 +39,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do operationId: "AccountController.create", requestBody: request_body("Parameters", AccountCreateRequest, required: true), responses: %{ - 200 => Operation.response("Account", "application/json", AccountCreateResponse) + 200 => Operation.response("Account", "application/json", AccountCreateResponse), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError), + 429 => Operation.response("Error", "application/json", ApiError) } } end @@ -65,7 +69,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do security: [%{"oAuth" => ["write:accounts"]}], requestBody: request_body("Parameters", AccountUpdateCredentialsRequest, required: true), responses: %{ - 200 => Operation.response("Account", "application/json", Account) + 200 => Operation.response("Account", "application/json", Account), + 403 => Operation.response("Error", "application/json", ApiError) } } end @@ -102,7 +107,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "View information about a profile.", parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], responses: %{ - 200 => Operation.response("Account", "application/json", Account) + 200 => Operation.response("Account", "application/json", Account), + 404 => Operation.response("Error", "application/json", ApiError) } } end @@ -140,7 +146,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do ) ] ++ pagination_params(), responses: %{ - 200 => Operation.response("Statuses", "application/json", StatusesResponse) + 200 => Operation.response("Statuses", "application/json", StatusesResponse), + 404 => Operation.response("Error", "application/json", ApiError) } } end @@ -204,7 +211,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do ) ], responses: %{ - 200 => Operation.response("Relationship", "application/json", AccountRelationship) + 200 => Operation.response("Relationship", "application/json", AccountRelationship), + 400 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end @@ -218,7 +227,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Unfollow the given account", parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], responses: %{ - 200 => Operation.response("Relationship", "application/json", AccountRelationship) + 200 => Operation.response("Relationship", "application/json", AccountRelationship), + 400 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end @@ -298,7 +309,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do security: [%{"oAuth" => ["follow", "write:follows"]}], requestBody: request_body("Parameters", AccountFollowsRequest, required: true), responses: %{ - 200 => Operation.response("Account", "application/json", AccountRelationship) + 200 => Operation.response("Account", "application/json", AccountRelationship), + 400 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index f57015254..d128feb30 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -41,7 +41,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do type: :object, properties: %{ allow_following_move: %Schema{type: :boolean}, - background_image: %Schema{type: :boolean, nullable: true}, + background_image: %Schema{type: :string, nullable: true}, chat_token: %Schema{type: :string}, confirmation_pending: %Schema{type: :boolean}, hide_favorites: %Schema{type: :boolean}, diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 4780081b2..eb97ae975 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -82,8 +82,9 @@ defmodule Pleroma.Web.ControllerHelper do end end - def assign_account_by_id(%{params: %{"id" => id}} = conn, _) do - case Pleroma.User.get_cached_by_id(id) do + def assign_account_by_id(conn, _) do + # TODO: use `conn.params[:id]` only after moving to OpenAPI + case Pleroma.User.get_cached_by_id(conn.params[:id] || conn.params["id"]) do %Pleroma.User{} = account -> assign(conn, :account, account) nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 93df79645..b1513001b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -26,6 +26,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI + plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError) + plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs) plug( @@ -83,8 +85,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(RateLimiter, [name: :app_account_creation] when action == :create) plug(:assign_account_by_id when action in @needs_account) - plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError) - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation -- cgit v1.2.3 From cc1e2e8d0f5fa27d051a0a21740a8052b95ce1a5 Mon Sep 17 00:00:00 2001 From: fence Date: Mon, 27 Apr 2020 19:11:03 +0200 Subject: requested changes to mongoose_im_controller.ex --- .../web/mongooseim/mongoose_im_controller.ex | 41 ++++++---------------- 1 file changed, 11 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index c15b4bfb8..7123153c5 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password) def user_exists(conn, %{"user" => username}) do - with %User{} <- Repo.get_by(User, nickname: username, local: true) do + with %User{} <- Repo.get_by(User, nickname: username, local: true, deactivated: false) do conn |> json(true) else @@ -26,41 +26,22 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do end def check_password(conn, %{"user" => username, "pass" => password}) do - user = Repo.get_by(User, nickname: username, local: true) - - state = case user do - nil -> nil - _ -> User.account_status(user) - end - - case state do - :deactivated -> + with %User{password_hash: password_hash, deactivated: false} <- + Repo.get_by(User, nickname: username, local: true), + true <- Pbkdf2.checkpw(password, password_hash) do + conn + |> json(true) + else + false -> conn - |> put_status(:not_found) + |> put_status(:forbidden) |> json(false) - :confirmation_pending -> + _ -> conn |> put_status(:not_found) |> json(false) - - _ -> - with %User{password_hash: password_hash} <- - user, - true <- Pbkdf2.checkpw(password, password_hash) do - conn - |> json(true) - else - false -> - conn - |> put_status(:forbidden) - |> json(false) - - _ -> - conn - |> put_status(:not_found) - |> json(false) - end end end end + -- cgit v1.2.3 From dda65f7799e9dfa2e7b87389848eeee10993a858 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 27 Apr 2020 22:55:05 +0400 Subject: Move single used schemas to operation schema --- .../web/api_spec/operations/account_operation.ex | 376 +++++++++++++++++++-- .../api_spec/operations/custom_emoji_operation.ex | 35 +- lib/pleroma/web/api_spec/schemas/account.ex | 4 +- .../web/api_spec/schemas/account_create_request.ex | 60 ---- .../api_spec/schemas/account_create_response.ex | 27 -- lib/pleroma/web/api_spec/schemas/account_emoji.ex | 29 -- .../api_spec/schemas/account_field_attribute.ex | 24 -- .../api_spec/schemas/account_follows_request.ex | 18 - .../web/api_spec/schemas/account_mute_request.ex | 24 -- .../schemas/account_relationships_response.ex | 58 ---- .../schemas/account_update_credentials_request.ex | 125 ------- .../web/api_spec/schemas/accounts_response.ex | 13 - lib/pleroma/web/api_spec/schemas/api_error.ex | 19 ++ lib/pleroma/web/api_spec/schemas/custom_emoji.ex | 30 -- lib/pleroma/web/api_spec/schemas/emoji.ex | 29 ++ lib/pleroma/web/api_spec/schemas/list.ex | 23 -- lib/pleroma/web/api_spec/schemas/lists_response.ex | 16 - lib/pleroma/web/api_spec/schemas/poll.ex | 4 +- lib/pleroma/web/api_spec/schemas/status.ex | 4 +- .../web/api_spec/schemas/statuses_response.ex | 13 - .../mastodon_api/controllers/account_controller.ex | 11 +- 21 files changed, 442 insertions(+), 500 deletions(-) delete mode 100644 lib/pleroma/web/api_spec/schemas/account_create_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_create_response.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_emoji.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_field_attribute.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_follows_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_mute_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_relationships_response.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/accounts_response.ex create mode 100644 lib/pleroma/web/api_spec/schemas/api_error.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/custom_emoji.ex create mode 100644 lib/pleroma/web/api_spec/schemas/emoji.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/list.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/lists_response.ex delete mode 100644 lib/pleroma/web/api_spec/schemas/statuses_response.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index bf8d21059..2efe6e901 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -7,18 +7,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do alias OpenApiSpex.Reference alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.Account - alias Pleroma.Web.ApiSpec.Schemas.ApiError - alias Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest - alias Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse - alias Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest - alias Pleroma.Web.ApiSpec.Schemas.AccountMuteRequest alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship - alias Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse - alias Pleroma.Web.ApiSpec.Schemas.AccountsResponse - alias Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest + alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.BooleanLike - alias Pleroma.Web.ApiSpec.Schemas.ListsResponse - alias Pleroma.Web.ApiSpec.Schemas.StatusesResponse + alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope import Pleroma.Web.ApiSpec.Helpers @@ -37,9 +30,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.", operationId: "AccountController.create", - requestBody: request_body("Parameters", AccountCreateRequest, required: true), + requestBody: request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("Account", "application/json", AccountCreateResponse), + 200 => Operation.response("Account", "application/json", create_response()), 400 => Operation.response("Error", "application/json", ApiError), 403 => Operation.response("Error", "application/json", ApiError), 429 => Operation.response("Error", "application/json", ApiError) @@ -67,7 +60,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Update the user's display and preferences.", operationId: "AccountController.update_credentials", security: [%{"oAuth" => ["write:accounts"]}], - requestBody: request_body("Parameters", AccountUpdateCredentialsRequest, required: true), + requestBody: request_body("Parameters", update_creadentials_request(), required: true), responses: %{ 200 => Operation.response("Account", "application/json", Account), 403 => Operation.response("Error", "application/json", ApiError) @@ -94,7 +87,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do ) ], responses: %{ - 200 => Operation.response("Account", "application/json", AccountRelationshipsResponse) + 200 => Operation.response("Account", "application/json", array_of_relationships()) } } end @@ -146,7 +139,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do ) ] ++ pagination_params(), responses: %{ - 200 => Operation.response("Statuses", "application/json", StatusesResponse), + 200 => Operation.response("Statuses", "application/json", array_of_statuses()), 404 => Operation.response("Error", "application/json", ApiError) } } @@ -163,7 +156,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), responses: %{ - 200 => Operation.response("Accounts", "application/json", AccountsResponse) + 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } } end @@ -178,7 +171,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Accounts which the given account is following, if network is not hidden by the account owner.", parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), - responses: %{200 => Operation.response("Accounts", "application/json", AccountsResponse)} + responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())} } end @@ -190,7 +183,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do security: [%{"oAuth" => ["read:lists"]}], description: "User lists that you have added this account to.", parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], - responses: %{200 => Operation.response("Lists", "application/json", ListsResponse)} + responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())} } end @@ -240,7 +233,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do summary: "Mute", operationId: "AccountController.mute", security: [%{"oAuth" => ["follow", "write:mutes"]}], - requestBody: request_body("Parameters", AccountMuteRequest), + requestBody: request_body("Parameters", mute_request()), description: "Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).", parameters: [ @@ -307,7 +300,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do summary: "Follows", operationId: "AccountController.follows", security: [%{"oAuth" => ["follow", "write:follows"]}], - requestBody: request_body("Parameters", AccountFollowsRequest, required: true), + requestBody: request_body("Parameters", follows_request(), required: true), responses: %{ 200 => Operation.response("Account", "application/json", AccountRelationship), 400 => Operation.response("Error", "application/json", ApiError), @@ -324,7 +317,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Accounts the user has muted.", security: [%{"oAuth" => ["follow", "read:mutes"]}], responses: %{ - 200 => Operation.response("Accounts", "application/json", AccountsResponse) + 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } } end @@ -337,7 +330,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "View your blocks. See also accounts/:id/{block,unblock}", security: [%{"oAuth" => ["read:blocks"]}], responses: %{ - 200 => Operation.response("Accounts", "application/json", AccountsResponse) + 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } } end @@ -366,4 +359,343 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } } end + + defp create_request do + %Schema{ + title: "AccountCreateRequest", + description: "POST body for creating an account", + type: :object, + properties: %{ + reason: %Schema{ + type: :string, + description: + "Text that will be reviewed by moderators if registrations require manual approval" + }, + username: %Schema{type: :string, description: "The desired username for the account"}, + email: %Schema{ + type: :string, + description: + "The email address to be used for login. Required when `account_activation_required` is enabled.", + format: :email + }, + password: %Schema{ + type: :string, + description: "The password to be used for login", + format: :password + }, + agreement: %Schema{ + type: :boolean, + description: + "Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE." + }, + locale: %Schema{ + type: :string, + description: "The language of the confirmation email that will be sent" + }, + # Pleroma-specific properties: + fullname: %Schema{type: :string, description: "Full name"}, + bio: %Schema{type: :string, description: "Bio", default: ""}, + captcha_solution: %Schema{ + type: :string, + description: "Provider-specific captcha solution" + }, + captcha_token: %Schema{type: :string, description: "Provider-specific captcha token"}, + captcha_answer_data: %Schema{type: :string, description: "Provider-specific captcha data"}, + token: %Schema{ + type: :string, + description: "Invite token required when the registrations aren't public" + } + }, + required: [:username, :password, :agreement], + example: %{ + "username" => "cofe", + "email" => "cofe@example.com", + "password" => "secret", + "agreement" => "true", + "bio" => "☕️" + } + } + end + + defp create_response do + %Schema{ + title: "AccountCreateResponse", + description: "Response schema for an account", + type: :object, + properties: %{ + token_type: %Schema{type: :string}, + access_token: %Schema{type: :string}, + scope: %Schema{type: :array, items: %Schema{type: :string}}, + created_at: %Schema{type: :integer, format: :"date-time"} + }, + example: %{ + "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk", + "created_at" => 1_585_918_714, + "scope" => ["read", "write", "follow", "push"], + "token_type" => "Bearer" + } + } + end + + defp update_creadentials_request do + %Schema{ + title: "AccountUpdateCredentialsRequest", + description: "POST body for creating an account", + type: :object, + properties: %{ + bot: %Schema{ + type: :boolean, + description: "Whether the account has a bot flag." + }, + display_name: %Schema{ + type: :string, + description: "The display name to use for the profile." + }, + note: %Schema{type: :string, description: "The account bio."}, + avatar: %Schema{ + type: :string, + description: "Avatar image encoded using multipart/form-data", + format: :binary + }, + header: %Schema{ + type: :string, + description: "Header image encoded using multipart/form-data", + format: :binary + }, + locked: %Schema{ + type: :boolean, + description: "Whether manual approval of follow requests is required." + }, + fields_attributes: %Schema{ + oneOf: [ + %Schema{type: :array, items: attribute_field()}, + %Schema{type: :object, additionalProperties: %Schema{type: attribute_field()}} + ] + }, + # NOTE: `source` field is not supported + # + # source: %Schema{ + # type: :object, + # properties: %{ + # privacy: %Schema{type: :string}, + # sensitive: %Schema{type: :boolean}, + # language: %Schema{type: :string} + # } + # }, + + # Pleroma-specific fields + no_rich_text: %Schema{ + type: :boolean, + description: "html tags are stripped from all statuses requested from the API" + }, + hide_followers: %Schema{type: :boolean, description: "user's followers will be hidden"}, + hide_follows: %Schema{type: :boolean, description: "user's follows will be hidden"}, + hide_followers_count: %Schema{ + type: :boolean, + description: "user's follower count will be hidden" + }, + hide_follows_count: %Schema{ + type: :boolean, + description: "user's follow count will be hidden" + }, + hide_favorites: %Schema{ + type: :boolean, + description: "user's favorites timeline will be hidden" + }, + show_role: %Schema{ + type: :boolean, + description: "user's role (e.g admin, moderator) will be exposed to anyone in the + API" + }, + default_scope: VisibilityScope, + pleroma_settings_store: %Schema{ + type: :object, + description: "Opaque user settings to be saved on the backend." + }, + skip_thread_containment: %Schema{ + type: :boolean, + description: "Skip filtering out broken threads" + }, + allow_following_move: %Schema{ + type: :boolean, + description: "Allows automatically follow moved following accounts" + }, + pleroma_background_image: %Schema{ + type: :string, + description: "Sets the background image of the user.", + format: :binary + }, + discoverable: %Schema{ + type: :boolean, + description: + "Discovery of this account in search results and other services is allowed." + }, + actor_type: ActorType + }, + example: %{ + bot: false, + display_name: "cofe", + note: "foobar", + fields_attributes: [%{name: "foo", value: "bar"}], + no_rich_text: false, + hide_followers: true, + hide_follows: false, + hide_followers_count: false, + hide_follows_count: false, + hide_favorites: false, + show_role: false, + default_scope: "private", + pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}}, + skip_thread_containment: false, + allow_following_move: false, + discoverable: false, + actor_type: "Person" + } + } + end + + defp array_of_accounts do + %Schema{ + title: "ArrayOfAccounts", + type: :array, + items: Account + } + end + + defp array_of_relationships do + %Schema{ + title: "ArrayOfRelationships", + description: "Response schema for account relationships", + type: :array, + items: AccountRelationship, + example: [ + %{ + "id" => "1", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => false, + "blocked_by" => true, + "muting" => false, + "muting_notifications" => false, + "requested" => false, + "domain_blocking" => false, + "subscribing" => false, + "endorsed" => true + }, + %{ + "id" => "2", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => false, + "blocked_by" => true, + "muting" => true, + "muting_notifications" => false, + "requested" => true, + "domain_blocking" => false, + "subscribing" => false, + "endorsed" => false + }, + %{ + "id" => "3", + "following" => true, + "showing_reblogs" => true, + "followed_by" => true, + "blocking" => true, + "blocked_by" => false, + "muting" => true, + "muting_notifications" => false, + "requested" => false, + "domain_blocking" => true, + "subscribing" => true, + "endorsed" => false + } + ] + } + end + + defp follows_request do + %Schema{ + title: "AccountFollowsRequest", + description: "POST body for muting an account", + type: :object, + properties: %{ + uri: %Schema{type: :string, format: :uri} + }, + required: [:uri] + } + end + + defp mute_request do + %Schema{ + title: "AccountMuteRequest", + description: "POST body for muting an account", + type: :object, + properties: %{ + notifications: %Schema{ + type: :boolean, + description: "Mute notifications in addition to statuses? Defaults to true.", + default: true + } + }, + example: %{ + "notifications" => true + } + } + end + + defp list do + %Schema{ + title: "List", + description: "Response schema for a list", + type: :object, + properties: %{ + id: %Schema{type: :string}, + title: %Schema{type: :string} + }, + example: %{ + "id" => "123", + "title" => "my list" + } + } + end + + defp array_of_lists do + %Schema{ + title: "ArrayOfLists", + description: "Response schema for lists", + type: :array, + items: list(), + example: [ + %{"id" => "123", "title" => "my list"}, + %{"id" => "1337", "title" => "anotehr list"} + ] + } + end + + defp array_of_statuses do + %Schema{ + title: "ArrayOfStatuses", + type: :array, + items: Status + } + end + + defp attribute_field do + %Schema{ + title: "AccountAttributeField", + description: "Request schema for account custom fields", + type: :object, + properties: %{ + name: %Schema{type: :string}, + value: %Schema{type: :string} + }, + required: [:name, :value], + example: %{ + "name" => "Website", + "value" => "https://pleroma.com" + } + } + end end diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex index a117fe460..2f812ac77 100644 --- a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex +++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji + alias Pleroma.Web.ApiSpec.Schemas.Emoji def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -19,17 +19,17 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do description: "Returns custom emojis that are available on the server.", operationId: "CustomEmojiController.index", responses: %{ - 200 => Operation.response("Custom Emojis", "application/json", custom_emojis_resposnse()) + 200 => Operation.response("Custom Emojis", "application/json", resposnse()) } } end - defp custom_emojis_resposnse do + defp resposnse do %Schema{ title: "CustomEmojisResponse", description: "Response schema for custom emojis", type: :array, - items: CustomEmoji, + items: custom_emoji(), example: [ %{ "category" => "Fun", @@ -58,4 +58,31 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do ] } end + + defp custom_emoji do + %Schema{ + title: "CustomEmoji", + description: "Schema for a CustomEmoji", + allOf: [ + Emoji, + %Schema{ + type: :object, + properties: %{ + category: %Schema{type: :string}, + tags: %Schema{type: :array} + } + } + ], + example: %{ + "category" => "Fun", + "shortcode" => "aaaa", + "url" => + "https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png", + "static_url" => + "https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png", + "visible_in_picker" => true, + "tags" => ["Gif", "Fun"] + } + } + end end diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index d128feb30..d54e2158d 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -4,10 +4,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji alias Pleroma.Web.ApiSpec.Schemas.AccountField alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -24,7 +24,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do bot: %Schema{type: :boolean}, created_at: %Schema{type: :string, format: "date-time"}, display_name: %Schema{type: :string}, - emojis: %Schema{type: :array, items: AccountEmoji}, + emojis: %Schema{type: :array, items: Emoji}, fields: %Schema{type: :array, items: AccountField}, follow_requests_count: %Schema{type: :integer}, followers_count: %Schema{type: :integer}, diff --git a/lib/pleroma/web/api_spec/schemas/account_create_request.ex b/lib/pleroma/web/api_spec/schemas/account_create_request.ex deleted file mode 100644 index 49fa12159..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_create_request.ex +++ /dev/null @@ -1,60 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateRequest do - alias OpenApiSpex.Schema - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountCreateRequest", - description: "POST body for creating an account", - type: :object, - properties: %{ - reason: %Schema{ - type: :string, - description: - "Text that will be reviewed by moderators if registrations require manual approval" - }, - username: %Schema{type: :string, description: "The desired username for the account"}, - email: %Schema{ - type: :string, - description: - "The email address to be used for login. Required when `account_activation_required` is enabled.", - format: :email - }, - password: %Schema{ - type: :string, - description: "The password to be used for login", - format: :password - }, - agreement: %Schema{ - type: :boolean, - description: - "Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE." - }, - locale: %Schema{ - type: :string, - description: "The language of the confirmation email that will be sent" - }, - # Pleroma-specific properties: - fullname: %Schema{type: :string, description: "Full name"}, - bio: %Schema{type: :string, description: "Bio", default: ""}, - captcha_solution: %Schema{type: :string, description: "Provider-specific captcha solution"}, - captcha_token: %Schema{type: :string, description: "Provider-specific captcha token"}, - captcha_answer_data: %Schema{type: :string, description: "Provider-specific captcha data"}, - token: %Schema{ - type: :string, - description: "Invite token required when the registrations aren't public" - } - }, - required: [:username, :password, :agreement], - example: %{ - "username" => "cofe", - "email" => "cofe@example.com", - "password" => "secret", - "agreement" => "true", - "bio" => "☕️" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_create_response.ex b/lib/pleroma/web/api_spec/schemas/account_create_response.ex deleted file mode 100644 index 2237351a2..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_create_response.ex +++ /dev/null @@ -1,27 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountCreateResponse do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountCreateResponse", - description: "Response schema for an account", - type: :object, - properties: %{ - token_type: %Schema{type: :string}, - access_token: %Schema{type: :string}, - scope: %Schema{type: :array, items: %Schema{type: :string}}, - created_at: %Schema{type: :integer, format: :"date-time"} - }, - example: %{ - "access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk", - "created_at" => 1_585_918_714, - "scope" => ["read", "write", "follow", "push"], - "token_type" => "Bearer" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_emoji.ex b/lib/pleroma/web/api_spec/schemas/account_emoji.ex deleted file mode 100644 index 6c1d4d95c..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_emoji.ex +++ /dev/null @@ -1,29 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountEmoji do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountEmoji", - description: "Response schema for account custom fields", - type: :object, - properties: %{ - shortcode: %Schema{type: :string}, - url: %Schema{type: :string, format: :uri}, - static_url: %Schema{type: :string, format: :uri}, - visible_in_picker: %Schema{type: :boolean} - }, - example: %{ - "shortcode" => "fatyoshi", - "url" => - "https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png", - "static_url" => - "https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png", - "visible_in_picker" => true - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex b/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex deleted file mode 100644 index 89e483655..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_field_attribute.ex +++ /dev/null @@ -1,24 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountAttributeField do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountAttributeField", - description: "Request schema for account custom fields", - type: :object, - properties: %{ - name: %Schema{type: :string}, - value: %Schema{type: :string} - }, - required: [:name, :value], - example: %{ - "name" => "Website", - "value" => "https://pleroma.com" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_follows_request.ex b/lib/pleroma/web/api_spec/schemas/account_follows_request.ex deleted file mode 100644 index 19dce0cb2..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_follows_request.ex +++ /dev/null @@ -1,18 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountFollowsRequest do - alias OpenApiSpex.Schema - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountFollowsRequest", - description: "POST body for muting an account", - type: :object, - properties: %{ - uri: %Schema{type: :string, format: :uri} - }, - required: [:uri] - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_mute_request.ex b/lib/pleroma/web/api_spec/schemas/account_mute_request.ex deleted file mode 100644 index a61f6d04c..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_mute_request.ex +++ /dev/null @@ -1,24 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountMuteRequest do - alias OpenApiSpex.Schema - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountMuteRequest", - description: "POST body for muting an account", - type: :object, - properties: %{ - notifications: %Schema{ - type: :boolean, - description: "Mute notifications in addition to statuses? Defaults to true.", - default: true - } - }, - example: %{ - "notifications" => true - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex b/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex deleted file mode 100644 index 960e14db1..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_relationships_response.ex +++ /dev/null @@ -1,58 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationshipsResponse do - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountRelationshipsResponse", - description: "Response schema for account relationships", - type: :array, - items: Pleroma.Web.ApiSpec.Schemas.AccountRelationship, - example: [ - %{ - "id" => "1", - "following" => true, - "showing_reblogs" => true, - "followed_by" => true, - "blocking" => false, - "blocked_by" => true, - "muting" => false, - "muting_notifications" => false, - "requested" => false, - "domain_blocking" => false, - "subscribing" => false, - "endorsed" => true - }, - %{ - "id" => "2", - "following" => true, - "showing_reblogs" => true, - "followed_by" => true, - "blocking" => false, - "blocked_by" => true, - "muting" => true, - "muting_notifications" => false, - "requested" => true, - "domain_blocking" => false, - "subscribing" => false, - "endorsed" => false - }, - %{ - "id" => "3", - "following" => true, - "showing_reblogs" => true, - "followed_by" => true, - "blocking" => true, - "blocked_by" => false, - "muting" => true, - "muting_notifications" => false, - "requested" => false, - "domain_blocking" => true, - "subscribing" => true, - "endorsed" => false - } - ] - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex b/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex deleted file mode 100644 index 35220c78a..000000000 --- a/lib/pleroma/web/api_spec/schemas/account_update_credentials_request.ex +++ /dev/null @@ -1,125 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountUpdateCredentialsRequest do - alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Schemas.AccountAttributeField - alias Pleroma.Web.ApiSpec.Schemas.ActorType - alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountUpdateCredentialsRequest", - description: "POST body for creating an account", - type: :object, - properties: %{ - bot: %Schema{ - type: :boolean, - description: "Whether the account has a bot flag." - }, - display_name: %Schema{ - type: :string, - description: "The display name to use for the profile." - }, - note: %Schema{type: :string, description: "The account bio."}, - avatar: %Schema{ - type: :string, - description: "Avatar image encoded using multipart/form-data", - format: :binary - }, - header: %Schema{ - type: :string, - description: "Header image encoded using multipart/form-data", - format: :binary - }, - locked: %Schema{ - type: :boolean, - description: "Whether manual approval of follow requests is required." - }, - fields_attributes: %Schema{ - oneOf: [ - %Schema{type: :array, items: AccountAttributeField}, - %Schema{type: :object, additionalProperties: %Schema{type: AccountAttributeField}} - ] - }, - # NOTE: `source` field is not supported - # - # source: %Schema{ - # type: :object, - # properties: %{ - # privacy: %Schema{type: :string}, - # sensitive: %Schema{type: :boolean}, - # language: %Schema{type: :string} - # } - # }, - - # Pleroma-specific fields - no_rich_text: %Schema{ - type: :boolean, - description: "html tags are stripped from all statuses requested from the API" - }, - hide_followers: %Schema{type: :boolean, description: "user's followers will be hidden"}, - hide_follows: %Schema{type: :boolean, description: "user's follows will be hidden"}, - hide_followers_count: %Schema{ - type: :boolean, - description: "user's follower count will be hidden" - }, - hide_follows_count: %Schema{ - type: :boolean, - description: "user's follow count will be hidden" - }, - hide_favorites: %Schema{ - type: :boolean, - description: "user's favorites timeline will be hidden" - }, - show_role: %Schema{ - type: :boolean, - description: "user's role (e.g admin, moderator) will be exposed to anyone in the - API" - }, - default_scope: VisibilityScope, - pleroma_settings_store: %Schema{ - type: :object, - description: "Opaque user settings to be saved on the backend." - }, - skip_thread_containment: %Schema{ - type: :boolean, - description: "Skip filtering out broken threads" - }, - allow_following_move: %Schema{ - type: :boolean, - description: "Allows automatically follow moved following accounts" - }, - pleroma_background_image: %Schema{ - type: :string, - description: "Sets the background image of the user.", - format: :binary - }, - discoverable: %Schema{ - type: :boolean, - description: "Discovery of this account in search results and other services is allowed." - }, - actor_type: ActorType - }, - example: %{ - bot: false, - display_name: "cofe", - note: "foobar", - fields_attributes: [%{name: "foo", value: "bar"}], - no_rich_text: false, - hide_followers: true, - hide_follows: false, - hide_followers_count: false, - hide_follows_count: false, - hide_favorites: false, - show_role: false, - default_scope: "private", - pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}}, - skip_thread_containment: false, - allow_following_move: false, - discoverable: false, - actor_type: "Person" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/accounts_response.ex b/lib/pleroma/web/api_spec/schemas/accounts_response.ex deleted file mode 100644 index b714f59e7..000000000 --- a/lib/pleroma/web/api_spec/schemas/accounts_response.ex +++ /dev/null @@ -1,13 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.AccountsResponse do - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "AccountsResponse", - type: :array, - items: Pleroma.Web.ApiSpec.Schemas.Account - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/api_error.ex b/lib/pleroma/web/api_spec/schemas/api_error.ex new file mode 100644 index 000000000..5815df94c --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/api_error.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "ApiError", + description: "Response schema for API error", + type: :object, + properties: %{error: %Schema{type: :string}}, + example: %{ + "error" => "Something went wrong" + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/custom_emoji.ex b/lib/pleroma/web/api_spec/schemas/custom_emoji.ex deleted file mode 100644 index 5531b2081..000000000 --- a/lib/pleroma/web/api_spec/schemas/custom_emoji.ex +++ /dev/null @@ -1,30 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.CustomEmoji do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "CustomEmoji", - description: "Response schema for an CustomEmoji", - type: :object, - properties: %{ - shortcode: %Schema{type: :string}, - url: %Schema{type: :string}, - static_url: %Schema{type: :string}, - visible_in_picker: %Schema{type: :boolean}, - category: %Schema{type: :string}, - tags: %Schema{type: :array} - }, - example: %{ - "shortcode" => "aaaa", - "url" => "https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png", - "static_url" => - "https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png", - "visible_in_picker" => true - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/emoji.ex b/lib/pleroma/web/api_spec/schemas/emoji.ex new file mode 100644 index 000000000..26f35e648 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/emoji.ex @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Emoji do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Emoji", + description: "Response schema for an emoji", + type: :object, + properties: %{ + shortcode: %Schema{type: :string}, + url: %Schema{type: :string, format: :uri}, + static_url: %Schema{type: :string, format: :uri}, + visible_in_picker: %Schema{type: :boolean} + }, + example: %{ + "shortcode" => "fatyoshi", + "url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png", + "static_url" => + "https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png", + "visible_in_picker" => true + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex deleted file mode 100644 index f85fac2b8..000000000 --- a/lib/pleroma/web/api_spec/schemas/list.ex +++ /dev/null @@ -1,23 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.List do - alias OpenApiSpex.Schema - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "List", - description: "Response schema for a list", - type: :object, - properties: %{ - id: %Schema{type: :string}, - title: %Schema{type: :string} - }, - example: %{ - "id" => "123", - "title" => "my list" - } - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/lists_response.ex b/lib/pleroma/web/api_spec/schemas/lists_response.ex deleted file mode 100644 index 132454579..000000000 --- a/lib/pleroma/web/api_spec/schemas/lists_response.ex +++ /dev/null @@ -1,16 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.ListsResponse do - alias Pleroma.Web.ApiSpec.Schemas.List - - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "ListsResponse", - description: "Response schema for lists", - type: :array, - items: List - }) -end diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index 5fc9e889f..0474b550b 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID require OpenApiSpex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do multiple: %Schema{type: :boolean}, votes_count: %Schema{type: :integer}, voted: %Schema{type: :boolean}, - emojis: %Schema{type: :array, items: AccountEmoji}, + emojis: %Schema{type: :array, items: Emoji}, options: %Schema{ type: :array, items: %Schema{ diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index bf5f04691..aef0588d4 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.Account - alias Pleroma.Web.ApiSpec.Schemas.AccountEmoji + alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.Poll alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -41,7 +41,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do }, content: %Schema{type: :string, format: :html}, created_at: %Schema{type: :string, format: "date-time"}, - emojis: %Schema{type: :array, items: AccountEmoji}, + emojis: %Schema{type: :array, items: Emoji}, favourited: %Schema{type: :boolean}, favourites_count: %Schema{type: :integer}, id: FlakeID, diff --git a/lib/pleroma/web/api_spec/schemas/statuses_response.ex b/lib/pleroma/web/api_spec/schemas/statuses_response.ex deleted file mode 100644 index fb7c7e0aa..000000000 --- a/lib/pleroma/web/api_spec/schemas/statuses_response.ex +++ /dev/null @@ -1,13 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.Schemas.StatusesResponse do - require OpenApiSpex - - OpenApiSpex.schema(%{ - title: "StatusesResponse", - type: :array, - items: Pleroma.Web.ApiSpec.Schemas.Status - }) -end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index b1513001b..37adeec5f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -104,8 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :fullname ]) |> Map.put(:nickname, params.username) - |> Map.put(:fullname, params.fullname || params.username) - |> Map.put(:bio, params.bio || "") + |> Map.put(:fullname, Map.get(params, :fullname, params.username)) |> Map.put(:confirm, params.password) |> Map.put(:trusted_app, app.trusted) @@ -158,7 +157,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do params = params - |> Map.from_struct() |> Enum.filter(fn {_, value} -> not is_nil(value) end) |> Enum.into(%{}) @@ -217,11 +215,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do Enum.map(fields, fn {_, v} -> v end) else Enum.map(fields, fn - %Pleroma.Web.ApiSpec.Schemas.AccountAttributeField{} = field -> - %{"name" => field.name, "value" => field.value} - - field -> - field + %{} = field -> %{"name" => field.name, "value" => field.value} + field -> field end) end end -- cgit v1.2.3 From 5ff20793e739daa962cdc1623c01dc6ec1ff8a61 Mon Sep 17 00:00:00 2001 From: fence Date: Tue, 28 Apr 2020 01:29:31 +0200 Subject: formating --- lib/pleroma/web/mongooseim/mongoose_im_controller.ex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index 7123153c5..1ed6ee521 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -44,4 +44,3 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do end end end - -- cgit v1.2.3 From 4c0e53367acd74de04de070a5e33380f5e457163 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 28 Apr 2020 20:04:25 +0300 Subject: [#2349] Post-merge fix. --- lib/pleroma/web/api_spec/operations/account_operation.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 2efe6e901..64e2e43c4 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -294,13 +294,13 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def follows_operation do + def follow_by_uri_operation do %Operation{ tags: ["accounts"], - summary: "Follows", + summary: "Follow by URI", operationId: "AccountController.follows", security: [%{"oAuth" => ["follow", "write:follows"]}], - requestBody: request_body("Parameters", follows_request(), required: true), + requestBody: request_body("Parameters", follow_by_uri_request(), required: true), responses: %{ 200 => Operation.response("Account", "application/json", AccountRelationship), 400 => Operation.response("Error", "application/json", ApiError), @@ -615,7 +615,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - defp follows_request do + defp follow_by_uri_request do %Schema{ title: "AccountFollowsRequest", description: "POST body for muting an account", -- cgit v1.2.3 From 8cf3a32463856f91a4e64a5cd33b5538b67c25c9 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 30 Apr 2020 00:49:59 +0300 Subject: Add exlude_replies to OpenAPI spec for account timelines --- lib/pleroma/web/api_spec/operations/account_operation.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 64e2e43c4..d3e8bd484 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -131,6 +131,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Include statuses from muted acccounts." ), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), + Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"), Operation.parameter( :exclude_visibilities, :query, -- cgit v1.2.3 From 5839e67eb86d6d14b21222247ce8e113c3b26637 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 6 Feb 2020 18:01:12 +0300 Subject: return data only for updated emoji --- .../controllers/emoji_api_controller.ex | 89 +++++++++++++--------- 1 file changed, 51 insertions(+), 38 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index e01825b48..981bac4fa 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -385,23 +385,35 @@ keeping it in cache for #{div(cache_ms, 1000)}s") json(conn, new_data) end - defp get_filename(%{"filename" => filename}), do: filename + defp get_filename(%Plug.Upload{filename: filename}), do: filename + defp get_filename(url) when is_binary(url), do: Path.basename(url) - defp get_filename(%{"file" => file}) do - case file do - %Plug.Upload{filename: filename} -> filename - url when is_binary(url) -> Path.basename(url) + defp empty?(str), do: String.trim(str) == "" + + defp update_pack_file(updated_full_pack, pack_file_p) do + content = Jason.encode!(updated_full_pack, pretty: true) + + File.write!(pack_file_p, content) + end + + defp create_subdirs(file_path) do + if String.contains?(file_path, "/") do + file_path + |> Path.dirname() + |> File.mkdir_p!() end end - defp empty?(str), do: String.trim(str) == "" + defp pack_info(pack_name) do + dir = Path.join(emoji_dir_path(), pack_name) + json_path = Path.join(dir, "pack.json") - defp update_file_and_send(conn, updated_full_pack, pack_file_p) do - # Write the emoji pack file - File.write!(pack_file_p, Jason.encode!(updated_full_pack, pretty: true)) + json = + json_path + |> File.read!() + |> Jason.decode!() - # Return the modified file list - json(conn, updated_full_pack["files"]) + {dir, json_path, json} end @doc """ @@ -422,23 +434,25 @@ keeping it in cache for #{div(cache_ms, 1000)}s") # Add def update_file( conn, - %{"pack_name" => pack_name, "action" => "add", "shortcode" => shortcode} = params + %{"pack_name" => pack_name, "action" => "add"} = params ) do - pack_dir = Path.join(emoji_dir_path(), pack_name) - pack_file_p = Path.join(pack_dir, "pack.json") + shortcode = + if params["shortcode"] do + params["shortcode"] + else + filename = get_filename(params["file"]) + Path.basename(filename, Path.extname(filename)) + end - full_pack = Jason.decode!(File.read!(pack_file_p)) + {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) with {_, false} <- {:has_shortcode, Map.has_key?(full_pack["files"], shortcode)}, - filename <- get_filename(params), + filename <- params["filename"] || get_filename(params["file"]), false <- empty?(shortcode), - false <- empty?(filename) do - file_path = Path.join(pack_dir, filename) - + false <- empty?(filename), + file_path <- Path.join(pack_dir, filename) do # If the name contains directories, create them - if String.contains?(file_path, "/") do - File.mkdir_p!(Path.dirname(file_path)) - end + create_subdirs(file_path) case params["file"] do %Plug.Upload{path: upload_path} -> @@ -451,8 +465,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s") File.write!(file_path, file_contents) end - updated_full_pack = put_in(full_pack, ["files", shortcode], filename) - update_file_and_send(conn, updated_full_pack, pack_file_p) + full_pack + |> put_in(["files", shortcode], filename) + |> update_pack_file(pack_file_p) + + json(conn, %{shortcode => filename}) else {:has_shortcode, _} -> conn @@ -472,10 +489,7 @@ keeping it in cache for #{div(cache_ms, 1000)}s") "action" => "remove", "shortcode" => shortcode }) do - pack_dir = Path.join(emoji_dir_path(), pack_name) - pack_file_p = Path.join(pack_dir, "pack.json") - - full_pack = Jason.decode!(File.read!(pack_file_p)) + {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) if Map.has_key?(full_pack["files"], shortcode) do {emoji_file_path, updated_full_pack} = pop_in(full_pack, ["files", shortcode]) @@ -494,7 +508,8 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end end - update_file_and_send(conn, updated_full_pack, pack_file_p) + update_pack_file(updated_full_pack, pack_file_p) + json(conn, %{shortcode => full_pack["files"][shortcode]}) else conn |> put_status(:bad_request) @@ -507,10 +522,7 @@ keeping it in cache for #{div(cache_ms, 1000)}s") conn, %{"pack_name" => pack_name, "action" => "update", "shortcode" => shortcode} = params ) do - pack_dir = Path.join(emoji_dir_path(), pack_name) - pack_file_p = Path.join(pack_dir, "pack.json") - - full_pack = Jason.decode!(File.read!(pack_file_p)) + {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) with {_, true} <- {:has_shortcode, Map.has_key?(full_pack["files"], shortcode)}, %{"new_shortcode" => new_shortcode, "new_filename" => new_filename} <- params, @@ -522,9 +534,7 @@ keeping it in cache for #{div(cache_ms, 1000)}s") new_emoji_file_path = Path.join(pack_dir, new_filename) # If the name contains directories, create them - if String.contains?(new_emoji_file_path, "/") do - File.mkdir_p!(Path.dirname(new_emoji_file_path)) - end + create_subdirs(new_emoji_file_path) # Move/Rename the old filename to a new filename # These are probably on the same filesystem, so just rename should work @@ -540,8 +550,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end # Then, put in the new shortcode with the new path - updated_full_pack = put_in(updated_full_pack, ["files", new_shortcode], new_filename) - update_file_and_send(conn, updated_full_pack, pack_file_p) + updated_full_pack + |> put_in(["files", new_shortcode], new_filename) + |> update_pack_file(pack_file_p) + + json(conn, %{new_shortcode => new_filename}) else {:has_shortcode, _} -> conn -- cgit v1.2.3 From 342f55fb92c723acf7f53de2dae390b72051c94b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 28 Mar 2020 13:34:32 +0300 Subject: refactor emoji api with fixes --- lib/pleroma/emoji/pack.ex | 509 +++++++++++++++++ .../controllers/emoji_api_controller.ex | 612 ++++++--------------- lib/pleroma/web/router.ex | 3 +- 3 files changed, 677 insertions(+), 447 deletions(-) create mode 100644 lib/pleroma/emoji/pack.ex (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex new file mode 100644 index 000000000..21ed12c78 --- /dev/null +++ b/lib/pleroma/emoji/pack.ex @@ -0,0 +1,509 @@ +defmodule Pleroma.Emoji.Pack do + @derive {Jason.Encoder, only: [:files, :pack]} + defstruct files: %{}, + pack_file: nil, + path: nil, + pack: %{}, + name: nil + + @type t() :: %__MODULE__{ + files: %{String.t() => Path.t()}, + pack_file: Path.t(), + path: Path.t(), + pack: map(), + name: String.t() + } + + alias Pleroma.Emoji + + @spec emoji_path() :: Path.t() + def emoji_path do + static = Pleroma.Config.get!([:instance, :static_dir]) + Path.join(static, "emoji") + end + + @spec create(String.t()) :: :ok | {:error, File.posix()} | {:error, :empty_values} + def create(name) when byte_size(name) > 0 do + dir = Path.join(emoji_path(), name) + + with :ok <- File.mkdir(dir) do + %__MODULE__{ + pack_file: Path.join(dir, "pack.json") + } + |> save_pack() + end + end + + def create(_), do: {:error, :empty_values} + + @spec show(String.t()) :: {:ok, t()} | {:loaded, nil} | {:error, :empty_values} + def show(name) when byte_size(name) > 0 do + with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)}, + {_, pack} <- validate_pack(pack) do + {:ok, pack} + end + end + + def show(_), do: {:error, :empty_values} + + @spec delete(String.t()) :: + {:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values} + def delete(name) when byte_size(name) > 0 do + emoji_path() + |> Path.join(name) + |> File.rm_rf() + end + + def delete(_), do: {:error, :empty_values} + + @spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) :: + {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} + def add_file(name, shortcode, filename, file) + when byte_size(name) > 0 and byte_size(shortcode) > 0 and byte_size(filename) > 0 do + with {_, nil} <- {:exists, Emoji.get(shortcode)}, + {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)} do + file_path = Path.join(pack.path, filename) + + create_subdirs(file_path) + + case file do + %Plug.Upload{path: upload_path} -> + # Copy the uploaded file from the temporary directory + File.copy!(upload_path, file_path) + + url when is_binary(url) -> + # Download and write the file + file_contents = Tesla.get!(url).body + File.write!(file_path, file_contents) + end + + files = Map.put(pack.files, shortcode, filename) + + updated_pack = %{pack | files: files} + + case save_pack(updated_pack) do + :ok -> + Emoji.reload() + {:ok, updated_pack} + + e -> + e + end + end + end + + def add_file(_, _, _, _), do: {:error, :empty_values} + + defp create_subdirs(file_path) do + if String.contains?(file_path, "/") do + file_path + |> Path.dirname() + |> File.mkdir_p!() + end + end + + @spec remove_file(String.t(), String.t()) :: + {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} + def remove_file(name, shortcode) when byte_size(name) > 0 and byte_size(shortcode) > 0 do + with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)}, + {_, {filename, files}} when not is_nil(filename) <- + {:exists, Map.pop(pack.files, shortcode)}, + emoji <- Path.join(pack.path, filename), + {_, true} <- {:exists, File.exists?(emoji)} do + emoji_dir = Path.dirname(emoji) + + File.rm!(emoji) + + if String.contains?(filename, "/") and File.ls!(emoji_dir) == [] do + File.rmdir!(emoji_dir) + end + + updated_pack = %{pack | files: files} + + case save_pack(updated_pack) do + :ok -> + Emoji.reload() + {:ok, updated_pack} + + e -> + e + end + end + end + + def remove_file(_, _), do: {:error, :empty_values} + + @spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) :: + {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} + def update_file(name, shortcode, new_shortcode, new_filename, force) + when byte_size(name) > 0 and byte_size(shortcode) > 0 and byte_size(new_shortcode) > 0 and + byte_size(new_filename) > 0 do + with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)}, + {_, {filename, files}} when not is_nil(filename) <- + {:exists, Map.pop(pack.files, shortcode)}, + {_, true} <- {:not_used, force or is_nil(Emoji.get(new_shortcode))} do + old_path = Path.join(pack.path, filename) + old_dir = Path.dirname(old_path) + new_path = Path.join(pack.path, new_filename) + + create_subdirs(new_path) + + :ok = File.rename(old_path, new_path) + + if String.contains?(filename, "/") and File.ls!(old_dir) == [] do + File.rmdir!(old_dir) + end + + files = Map.put(files, new_shortcode, new_filename) + + updated_pack = %{pack | files: files} + + case save_pack(updated_pack) do + :ok -> + Emoji.reload() + {:ok, updated_pack} + + e -> + e + end + end + end + + def update_file(_, _, _, _, _), do: {:error, :empty_values} + + @spec import_from_filesystem() :: {:ok, [String.t()]} | {:error, atom()} + def import_from_filesystem do + emoji_path = emoji_path() + + with {:ok, %{access: :read_write}} <- File.stat(emoji_path), + {:ok, results} <- File.ls(emoji_path) do + names = + results + |> Enum.map(&Path.join(emoji_path, &1)) + |> Enum.reject(fn path -> + File.dir?(path) and File.exists?(Path.join(path, "pack.json")) + end) + |> Enum.map(&write_pack_contents/1) + |> Enum.filter(& &1) + + {:ok, names} + else + {:ok, %{access: _}} -> {:error, :not_writable} + e -> e + end + end + + defp write_pack_contents(path) do + pack = %__MODULE__{ + files: files_from_path(path), + path: path, + pack_file: Path.join(path, "pack.json") + } + + case save_pack(pack) do + :ok -> Path.basename(path) + _ -> nil + end + end + + defp files_from_path(path) do + txt_path = Path.join(path, "emoji.txt") + + if File.exists?(txt_path) do + # There's an emoji.txt file, it's likely from a pack installed by the pack manager. + # Make a pack.json file from the contents of that emoji.txt file + + # FIXME: Copy-pasted from Pleroma.Emoji/load_from_file_stream/2 + + # Create a map of shortcodes to filenames from emoji.txt + File.read!(txt_path) + |> String.split("\n") + |> Enum.map(&String.trim/1) + |> Enum.map(fn line -> + case String.split(line, ~r/,\s*/) do + # This matches both strings with and without tags + # and we don't care about tags here + [name, file | _] -> + file_dir_name = Path.dirname(file) + + file = + if String.ends_with?(path, file_dir_name) do + Path.basename(file) + else + file + end + + {name, file} + + _ -> + nil + end + end) + |> Enum.filter(& &1) + |> Enum.into(%{}) + else + # If there's no emoji.txt, assume all files + # that are of certain extensions from the config are emojis and import them all + pack_extensions = Pleroma.Config.get!([:emoji, :pack_extensions]) + Emoji.Loader.make_shortcode_to_file_map(path, pack_extensions) + end + end + + @spec list_remote_packs(String.t()) :: {:ok, map()} + def list_remote_packs(url) do + uri = + url + |> String.trim() + |> URI.parse() + + with {_, true} <- {:shareable, shareable_packs_available?(uri)} do + packs = + uri + |> URI.merge("/api/pleroma/emoji/packs") + |> to_string() + |> Tesla.get!() + |> Map.get(:body) + |> Jason.decode!() + + {:ok, packs} + end + end + + @spec list_local_packs() :: {:ok, map()} + def list_local_packs do + emoji_path = emoji_path() + + # Create the directory first if it does not exist. This is probably the first request made + # with the API so it should be sufficient + with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_path)}, + {:ls, {:ok, results}} <- {:ls, File.ls(emoji_path)} do + packs = + results + |> Enum.map(&load_pack/1) + |> Enum.filter(& &1) + |> Enum.map(&validate_pack/1) + |> Map.new() + + {:ok, packs} + end + end + + defp validate_pack(pack) do + if downloadable?(pack) do + archive = fetch_archive(pack) + archive_sha = :crypto.hash(:sha256, archive) |> Base.encode16() + + info = + pack.pack + |> Map.put("can-download", true) + |> Map.put("download-sha256", archive_sha) + + {pack.name, Map.put(pack, :pack, info)} + else + info = Map.put(pack.pack, "can-download", false) + {pack.name, Map.put(pack, :pack, info)} + end + end + + defp downloadable?(pack) do + # If the pack is set as shared, check if it can be downloaded + # That means that when asked, the pack can be packed and sent to the remote + # Otherwise, they'd have to download it from external-src + pack.pack["share-files"] && + Enum.all?(pack.files, fn {_, file} -> + File.exists?(Path.join(pack.path, file)) + end) + end + + @spec download(String.t()) :: {:ok, binary()} + def download(name) do + with {_, %__MODULE__{} = pack} <- {:exists?, load_pack(name)}, + {_, true} <- {:can_download?, downloadable?(pack)} do + {:ok, fetch_archive(pack)} + end + end + + defp fetch_archive(pack) do + hash = :crypto.hash(:md5, File.read!(pack.pack_file)) + + case Cachex.get!(:emoji_packs_cache, pack.name) do + %{hash: ^hash, pack_data: archive} -> + archive + + _ -> + create_archive_and_cache(pack, hash) + end + end + + defp create_archive_and_cache(pack, hash) do + files = ['pack.json' | Enum.map(pack.files, fn {_, file} -> to_charlist(file) end)] + + {:ok, {_, result}} = + :zip.zip('#{pack.name}.zip', files, [:memory, cwd: to_charlist(pack.path)]) + + ttl_per_file = Pleroma.Config.get!([:emoji, :shared_pack_cache_seconds_per_file]) + overall_ttl = :timer.seconds(ttl_per_file * Enum.count(files)) + + Cachex.put!( + :emoji_packs_cache, + pack.name, + # if pack.json MD5 changes, the cache is not valid anymore + %{hash: hash, pack_data: result}, + # Add a minute to cache time for every file in the pack + ttl: overall_ttl + ) + + result + end + + @spec download_from_source(String.t(), String.t(), String.t()) :: :ok + def download_from_source(name, url, as) do + uri = + url + |> String.trim() + |> URI.parse() + + with {_, true} <- {:shareable, shareable_packs_available?(uri)} do + # TODO: why do we load all packs, if we know the name of pack we need + remote_pack = + uri + |> URI.merge("/api/pleroma/emoji/packs/#{name}") + |> to_string() + |> Tesla.get!() + |> Map.get(:body) + |> Jason.decode!() + + result = + case remote_pack["pack"] do + %{"share-files" => true, "can-download" => true, "download-sha256" => sha} -> + {:ok, + %{ + sha: sha, + url: + URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/download_shared") |> to_string() + }} + + %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) -> + {:ok, + %{ + sha: sha, + url: src, + fallback: true + }} + + _ -> + {:error, + "The pack was not set as shared and there is no fallback src to download from"} + end + + with {:ok, %{sha: sha, url: url} = pinfo} <- result, + %{body: archive} <- Tesla.get!(url), + {_, true} <- {:checksum, Base.decode16!(sha) == :crypto.hash(:sha256, archive)} do + local_name = as || name + + path = Path.join(emoji_path(), local_name) + + pack = %__MODULE__{ + name: local_name, + path: path, + files: remote_pack["files"], + pack_file: Path.join(path, "pack.json") + } + + File.mkdir_p!(pack.path) + + files = Enum.map(remote_pack["files"], fn {_, path} -> to_charlist(path) end) + # Fallback cannot contain a pack.json file + files = if pinfo[:fallback], do: files, else: ['pack.json' | files] + + {:ok, _} = :zip.unzip(archive, cwd: to_charlist(pack.path), file_list: files) + + # Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256 + # in it to depend on itself + if pinfo[:fallback] do + save_pack(pack) + end + + :ok + end + end + end + + defp save_pack(pack), do: File.write(pack.pack_file, Jason.encode!(pack, pretty: true)) + + @spec save_metadata(map(), t()) :: {:ok, t()} | {:error, File.posix()} + def save_metadata(metadata, %__MODULE__{} = pack) do + pack = Map.put(pack, :pack, metadata) + + with :ok <- save_pack(pack) do + {:ok, pack} + end + end + + @spec update_metadata(String.t(), map()) :: {:ok, t()} | {:error, File.posix()} + def update_metadata(name, data) do + pack = load_pack(name) + + fb_sha_changed? = + not is_nil(data["fallback-src"]) and data["fallback-src"] != pack.pack["fallback-src"] + + with {_, true} <- {:update?, fb_sha_changed?}, + {:ok, %{body: zip}} <- Tesla.get(data["fallback-src"]), + {:ok, f_list} <- :zip.unzip(zip, [:memory]), + {_, true} <- {:has_all_files?, has_all_files?(pack.files, f_list)} do + fallback_sha = :crypto.hash(:sha256, zip) |> Base.encode16() + + data + |> Map.put("fallback-src-sha256", fallback_sha) + |> save_metadata(pack) + else + {:update?, _} -> save_metadata(data, pack) + e -> e + end + end + + # Check if all files from the pack.json are in the archive + defp has_all_files?(files, f_list) do + Enum.all?(files, fn {_, from_manifest} -> + List.keyfind(f_list, to_charlist(from_manifest), 0) + end) + end + + @spec load_pack(String.t()) :: t() | nil + def load_pack(name) do + pack_file = Path.join([emoji_path(), name, "pack.json"]) + + if File.exists?(pack_file) do + pack_file + |> File.read!() + |> from_json() + |> Map.put(:pack_file, pack_file) + |> Map.put(:path, Path.dirname(pack_file)) + |> Map.put(:name, name) + end + end + + defp from_json(json) do + map = Jason.decode!(json) + + struct(__MODULE__, %{files: map["files"], pack: map["pack"]}) + end + + defp shareable_packs_available?(uri) do + uri + |> URI.merge("/.well-known/nodeinfo") + |> to_string() + |> Tesla.get!() + |> Map.get(:body) + |> Jason.decode!() + |> Map.get("links") + |> List.last() + |> Map.get("href") + # Get the actual nodeinfo address and fetch it + |> Tesla.get!() + |> Map.get(:body) + |> Jason.decode!() + |> get_in(["metadata", "features"]) + |> Enum.member?("shareable_emoji_packs") + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 981bac4fa..9fa857474 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -1,18 +1,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do use Pleroma.Web, :controller - alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug - alias Pleroma.Plugs.OAuthScopesPlug - - require Logger + alias Pleroma.Emoji.Pack plug( - OAuthScopesPlug, + Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [ :create, :delete, - :save_from, + :download_from, :import_from_fs, :update_file, :update_metadata @@ -21,17 +18,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do plug( :skip_plug, - [OAuthScopesPlug, ExpectPublicOrAuthenticatedCheckPlug] + [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug] when action in [:download_shared, :list_packs, :list_from] ) - defp emoji_dir_path do - Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) - end - @doc """ Lists packs from the remote instance. @@ -39,17 +29,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do be done by the server """ def list_from(conn, %{"instance_address" => address}) do - address = String.trim(address) - - if shareable_packs_available(address) do - list_resp = - "#{address}/api/pleroma/emoji/packs" |> Tesla.get!() |> Map.get(:body) |> Jason.decode!() - - json(conn, list_resp) + with {:ok, packs} <- Pack.list_remote_packs(address) do + json(conn, packs) else - conn - |> put_status(:internal_server_error) - |> json(%{error: "The requested instance does not support sharing emoji packs"}) + {:shareable, _} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "The requested instance does not support sharing emoji packs"}) end end @@ -60,113 +46,44 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do a map of "pack directory name" to pack.json contents. """ def list_packs(conn, _params) do - # Create the directory first if it does not exist. This is probably the first request made - # with the API so it should be sufficient - with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_dir_path())}, - {:ls, {:ok, results}} <- {:ls, File.ls(emoji_dir_path())} do - pack_infos = - results - |> Enum.filter(&has_pack_json?/1) - |> Enum.map(&load_pack/1) - # Check if all the files are in place and can be sent - |> Enum.map(&validate_pack/1) - # Transform into a map of pack-name => pack-data - |> Enum.into(%{}) - - json(conn, pack_infos) + emoji_path = + Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + + with {:ok, packs} <- Pack.list_local_packs() do + json(conn, packs) else {:create_dir, {:error, e}} -> conn |> put_status(:internal_server_error) - |> json(%{error: "Failed to create the emoji pack directory at #{emoji_dir_path()}: #{e}"}) + |> json(%{error: "Failed to create the emoji pack directory at #{emoji_path}: #{e}"}) {:ls, {:error, e}} -> conn |> put_status(:internal_server_error) |> json(%{ - error: - "Failed to get the contents of the emoji pack directory at #{emoji_dir_path()}: #{e}" + error: "Failed to get the contents of the emoji pack directory at #{emoji_path}: #{e}" }) end end - defp has_pack_json?(file) do - dir_path = Path.join(emoji_dir_path(), file) - # Filter to only use the pack.json packs - File.dir?(dir_path) and File.exists?(Path.join(dir_path, "pack.json")) - end - - defp load_pack(pack_name) do - pack_path = Path.join(emoji_dir_path(), pack_name) - pack_file = Path.join(pack_path, "pack.json") - - {pack_name, Jason.decode!(File.read!(pack_file))} - end - - defp validate_pack({name, pack}) do - pack_path = Path.join(emoji_dir_path(), name) - - if can_download?(pack, pack_path) do - archive_for_sha = make_archive(name, pack, pack_path) - archive_sha = :crypto.hash(:sha256, archive_for_sha) |> Base.encode16() + def show(conn, %{"name" => name}) do + name = String.trim(name) - pack = - pack - |> put_in(["pack", "can-download"], true) - |> put_in(["pack", "download-sha256"], archive_sha) - - {name, pack} + with {:ok, pack} <- Pack.show(name) do + json(conn, pack) else - {name, put_in(pack, ["pack", "can-download"], false)} - end - end - - defp can_download?(pack, pack_path) do - # If the pack is set as shared, check if it can be downloaded - # That means that when asked, the pack can be packed and sent to the remote - # Otherwise, they'd have to download it from external-src - pack["pack"]["share-files"] && - Enum.all?(pack["files"], fn {_, path} -> - File.exists?(Path.join(pack_path, path)) - end) - end - - defp create_archive_and_cache(name, pack, pack_dir, md5) do - files = - ['pack.json'] ++ - (pack["files"] |> Enum.map(fn {_, path} -> to_charlist(path) end)) - - {:ok, {_, zip_result}} = :zip.zip('#{name}.zip', files, [:memory, cwd: to_charlist(pack_dir)]) - - cache_seconds_per_file = Pleroma.Config.get!([:emoji, :shared_pack_cache_seconds_per_file]) - cache_ms = :timer.seconds(cache_seconds_per_file * Enum.count(files)) - - Cachex.put!( - :emoji_packs_cache, - name, - # if pack.json MD5 changes, the cache is not valid anymore - %{pack_json_md5: md5, pack_data: zip_result}, - # Add a minute to cache time for every file in the pack - ttl: cache_ms - ) - - Logger.debug("Created an archive for the '#{name}' emoji pack, \ -keeping it in cache for #{div(cache_ms, 1000)}s") - - zip_result - end - - defp make_archive(name, pack, pack_dir) do - # Having a different pack.json md5 invalidates cache - pack_file_md5 = :crypto.hash(:md5, File.read!(Path.join(pack_dir, "pack.json"))) - - case Cachex.get!(:emoji_packs_cache, name) do - %{pack_file_md5: ^pack_file_md5, pack_data: zip_result} -> - Logger.debug("Using cache for the '#{name}' shared emoji pack") - zip_result + {:loaded, _} -> + conn + |> put_status(:not_found) + |> json(%{error: "Pack #{name} does not exist"}) - _ -> - create_archive_and_cache(name, pack, pack_dir, pack_file_md5) + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name cannot be empty"}) end end @@ -175,21 +92,15 @@ keeping it in cache for #{div(cache_ms, 1000)}s") to download packs that the instance shares. """ def download_shared(conn, %{"name" => name}) do - pack_dir = Path.join(emoji_dir_path(), name) - pack_file = Path.join(pack_dir, "pack.json") - - with {_, true} <- {:exists?, File.exists?(pack_file)}, - pack = Jason.decode!(File.read!(pack_file)), - {_, true} <- {:can_download?, can_download?(pack, pack_dir)} do - zip_result = make_archive(name, pack, pack_dir) - send_download(conn, {:binary, zip_result}, filename: "#{name}.zip") + with {:ok, archive} <- Pack.download(name) do + send_download(conn, {:binary, archive}, filename: "#{name}.zip") else {:can_download?, _} -> conn |> put_status(:forbidden) |> json(%{ - error: "Pack #{name} cannot be downloaded from this instance, either pack sharing\ - was disabled for this pack or some files are missing" + error: + "Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" }) {:exists?, _} -> @@ -199,22 +110,6 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end end - defp shareable_packs_available(address) do - "#{address}/.well-known/nodeinfo" - |> Tesla.get!() - |> Map.get(:body) - |> Jason.decode!() - |> Map.get("links") - |> List.last() - |> Map.get("href") - # Get the actual nodeinfo address and fetch it - |> Tesla.get!() - |> Map.get(:body) - |> Jason.decode!() - |> get_in(["metadata", "features"]) - |> Enum.member?("shareable_emoji_packs") - end - @doc """ An admin endpoint to request downloading and storing a pack named `pack_name` from the instance `instance_address`. @@ -222,74 +117,24 @@ keeping it in cache for #{div(cache_ms, 1000)}s") If the requested instance's admin chose to share the pack, it will be downloaded from that instance, otherwise it will be downloaded from the fallback source, if there is one. """ - def save_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do - address = String.trim(address) - - if shareable_packs_available(address) do - full_pack = - "#{address}/api/pleroma/emoji/packs/list" - |> Tesla.get!() - |> Map.get(:body) - |> Jason.decode!() - |> Map.get(name) - - pack_info_res = - case full_pack["pack"] do - %{"share-files" => true, "can-download" => true, "download-sha256" => sha} -> - {:ok, - %{ - sha: sha, - uri: "#{address}/api/pleroma/emoji/packs/download_shared/#{name}" - }} - - %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) -> - {:ok, - %{ - sha: sha, - uri: src, - fallback: true - }} - - _ -> - {:error, - "The pack was not set as shared and there is no fallback src to download from"} - end - - with {:ok, %{sha: sha, uri: uri} = pinfo} <- pack_info_res, - %{body: emoji_archive} <- Tesla.get!(uri), - {_, true} <- {:checksum, Base.decode16!(sha) == :crypto.hash(:sha256, emoji_archive)} do - local_name = data["as"] || name - pack_dir = Path.join(emoji_dir_path(), local_name) - File.mkdir_p!(pack_dir) - - files = Enum.map(full_pack["files"], fn {_, path} -> to_charlist(path) end) - # Fallback cannot contain a pack.json file - files = if pinfo[:fallback], do: files, else: ['pack.json'] ++ files - - {:ok, _} = :zip.unzip(emoji_archive, cwd: to_charlist(pack_dir), file_list: files) - - # Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256 - # in it to depend on itself - if pinfo[:fallback] do - pack_file_path = Path.join(pack_dir, "pack.json") - - File.write!(pack_file_path, Jason.encode!(full_pack, pretty: true)) - end - - json(conn, "ok") - else - {:error, e} -> - conn |> put_status(:internal_server_error) |> json(%{error: e}) - - {:checksum, _} -> - conn - |> put_status(:internal_server_error) - |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) - end + def download_from(conn, %{"instance_address" => address, "pack_name" => name} = params) do + with :ok <- Pack.download_from_source(name, address, params["as"]) do + json(conn, "ok") else - conn - |> put_status(:internal_server_error) - |> json(%{error: "The requested instance does not support sharing emoji packs"}) + {:shareable, _} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "The requested instance does not support sharing emoji packs"}) + + {:checksum, _} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) + + {:error, e} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: e}) end end @@ -297,23 +142,27 @@ keeping it in cache for #{div(cache_ms, 1000)}s") Creates an empty pack named `name` which then can be updated via the admin UI. """ def create(conn, %{"name" => name}) do - pack_dir = Path.join(emoji_dir_path(), name) + name = String.trim(name) - if not File.exists?(pack_dir) do - File.mkdir_p!(pack_dir) - - pack_file_p = Path.join(pack_dir, "pack.json") + with :ok <- Pack.create(name) do + json(conn, "ok") + else + {:error, :eexist} -> + conn + |> put_status(:conflict) + |> json(%{error: "A pack named \"#{name}\" already exists"}) - File.write!( - pack_file_p, - Jason.encode!(%{pack: %{}, files: %{}}, pretty: true) - ) + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name cannot be empty"}) - conn |> json("ok") - else - conn - |> put_status(:conflict) - |> json(%{error: "A pack named \"#{name}\" already exists"}) + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while creating pack." + ) end end @@ -321,11 +170,20 @@ keeping it in cache for #{div(cache_ms, 1000)}s") Deletes the pack `name` and all it's files. """ def delete(conn, %{"name" => name}) do - pack_dir = Path.join(emoji_dir_path(), name) + name = String.trim(name) + + with {:ok, deleted} when deleted != [] <- Pack.delete(name) do + json(conn, "ok") + else + {:ok, []} -> + conn + |> put_status(:not_found) + |> json(%{error: "Pack #{name} does not exist"}) - case File.rm_rf(pack_dir) do - {:ok, _} -> - conn |> json("ok") + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name cannot be empty"}) {:error, _, _} -> conn @@ -340,82 +198,23 @@ keeping it in cache for #{div(cache_ms, 1000)}s") `new_data` is the new metadata for the pack, that will replace the old metadata. """ def update_metadata(conn, %{"pack_name" => name, "new_data" => new_data}) do - pack_file_p = Path.join([emoji_dir_path(), name, "pack.json"]) - - full_pack = Jason.decode!(File.read!(pack_file_p)) - - # The new fallback-src is in the new data and it's not the same as it was in the old data - should_update_fb_sha = - not is_nil(new_data["fallback-src"]) and - new_data["fallback-src"] != full_pack["pack"]["fallback-src"] - - with {_, true} <- {:should_update?, should_update_fb_sha}, - %{body: pack_arch} <- Tesla.get!(new_data["fallback-src"]), - {:ok, flist} <- :zip.unzip(pack_arch, [:memory]), - {_, true} <- {:has_all_files?, has_all_files?(full_pack, flist)} do - fallback_sha = :crypto.hash(:sha256, pack_arch) |> Base.encode16() - - new_data = Map.put(new_data, "fallback-src-sha256", fallback_sha) - update_metadata_and_send(conn, full_pack, new_data, pack_file_p) + with {:ok, pack} <- Pack.update_metadata(name, new_data) do + json(conn, pack.pack) else - {:should_update?, _} -> - update_metadata_and_send(conn, full_pack, new_data, pack_file_p) - {:has_all_files?, _} -> conn |> put_status(:bad_request) |> json(%{error: "The fallback archive does not have all files specified in pack.json"}) - end - end - - # Check if all files from the pack.json are in the archive - defp has_all_files?(%{"files" => files}, flist) do - Enum.all?(files, fn {_, from_manifest} -> - Enum.find(flist, fn {from_archive, _} -> - to_string(from_archive) == from_manifest - end) - end) - end - - defp update_metadata_and_send(conn, full_pack, new_data, pack_file_p) do - full_pack = Map.put(full_pack, "pack", new_data) - File.write!(pack_file_p, Jason.encode!(full_pack, pretty: true)) - - # Send new data back with fallback sha filled - json(conn, new_data) - end - - defp get_filename(%Plug.Upload{filename: filename}), do: filename - defp get_filename(url) when is_binary(url), do: Path.basename(url) - - defp empty?(str), do: String.trim(str) == "" - - defp update_pack_file(updated_full_pack, pack_file_p) do - content = Jason.encode!(updated_full_pack, pretty: true) - - File.write!(pack_file_p, content) - end - defp create_subdirs(file_path) do - if String.contains?(file_path, "/") do - file_path - |> Path.dirname() - |> File.mkdir_p!() + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while updating pack metadata." + ) end end - defp pack_info(pack_name) do - dir = Path.join(emoji_dir_path(), pack_name) - json_path = Path.join(dir, "pack.json") - - json = - json_path - |> File.read!() - |> Jason.decode!() - - {dir, json_path, json} - end - @doc """ Updates a file in a pack. @@ -436,50 +235,33 @@ keeping it in cache for #{div(cache_ms, 1000)}s") conn, %{"pack_name" => pack_name, "action" => "add"} = params ) do - shortcode = - if params["shortcode"] do - params["shortcode"] - else - filename = get_filename(params["file"]) - Path.basename(filename, Path.extname(filename)) - end - - {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) - - with {_, false} <- {:has_shortcode, Map.has_key?(full_pack["files"], shortcode)}, - filename <- params["filename"] || get_filename(params["file"]), - false <- empty?(shortcode), - false <- empty?(filename), - file_path <- Path.join(pack_dir, filename) do - # If the name contains directories, create them - create_subdirs(file_path) - - case params["file"] do - %Plug.Upload{path: upload_path} -> - # Copy the uploaded file from the temporary directory - File.copy!(upload_path, file_path) - - url when is_binary(url) -> - # Download and write the file - file_contents = Tesla.get!(url).body - File.write!(file_path, file_contents) - end - - full_pack - |> put_in(["files", shortcode], filename) - |> update_pack_file(pack_file_p) - - json(conn, %{shortcode => filename}) + filename = params["filename"] || get_filename(params["file"]) + shortcode = params["shortcode"] || Path.basename(filename, Path.extname(filename)) + + with {:ok, pack} <- Pack.add_file(pack_name, shortcode, filename, params["file"]) do + json(conn, pack.files) else - {:has_shortcode, _} -> + {:exists, _} -> conn |> put_status(:conflict) |> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) - true -> + {:loaded, _} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) + + {:error, :empty_values} -> conn |> put_status(:bad_request) - |> json(%{error: "shortcode or filename cannot be empty"}) + |> json(%{error: "pack name, shortcode or filename cannot be empty"}) + + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while adding file to pack." + ) end end @@ -489,87 +271,74 @@ keeping it in cache for #{div(cache_ms, 1000)}s") "action" => "remove", "shortcode" => shortcode }) do - {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) - - if Map.has_key?(full_pack["files"], shortcode) do - {emoji_file_path, updated_full_pack} = pop_in(full_pack, ["files", shortcode]) - - emoji_file_path = Path.join(pack_dir, emoji_file_path) - - # Delete the emoji file - File.rm!(emoji_file_path) + with {:ok, pack} <- Pack.remove_file(pack_name, shortcode) do + json(conn, pack.files) + else + {:exists, _} -> + conn + |> put_status(:bad_request) + |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - # If the old directory has no more files, remove it - if String.contains?(emoji_file_path, "/") do - dir = Path.dirname(emoji_file_path) + {:loaded, _} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack \"#{pack_name}\" is not found"}) - if Enum.empty?(File.ls!(dir)) do - File.rmdir!(dir) - end - end + {:error, :empty_values} -> + conn + |> put_status(:bad_request) + |> json(%{error: "pack name or shortcode cannot be empty"}) - update_pack_file(updated_full_pack, pack_file_p) - json(conn, %{shortcode => full_pack["files"][shortcode]}) - else - conn - |> put_status(:bad_request) - |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while removing file from pack." + ) end end # Update def update_file( conn, - %{"pack_name" => pack_name, "action" => "update", "shortcode" => shortcode} = params + %{"pack_name" => name, "action" => "update", "shortcode" => shortcode} = params ) do - {pack_dir, pack_file_p, full_pack} = pack_info(pack_name) - - with {_, true} <- {:has_shortcode, Map.has_key?(full_pack["files"], shortcode)}, - %{"new_shortcode" => new_shortcode, "new_filename" => new_filename} <- params, - false <- empty?(new_shortcode), - false <- empty?(new_filename) do - # First, remove the old shortcode, saving the old path - {old_emoji_file_path, updated_full_pack} = pop_in(full_pack, ["files", shortcode]) - old_emoji_file_path = Path.join(pack_dir, old_emoji_file_path) - new_emoji_file_path = Path.join(pack_dir, new_filename) - - # If the name contains directories, create them - create_subdirs(new_emoji_file_path) - - # Move/Rename the old filename to a new filename - # These are probably on the same filesystem, so just rename should work - :ok = File.rename(old_emoji_file_path, new_emoji_file_path) - - # If the old directory has no more files, remove it - if String.contains?(old_emoji_file_path, "/") do - dir = Path.dirname(old_emoji_file_path) - - if Enum.empty?(File.ls!(dir)) do - File.rmdir!(dir) - end - end - - # Then, put in the new shortcode with the new path - updated_full_pack - |> put_in(["files", new_shortcode], new_filename) - |> update_pack_file(pack_file_p) - - json(conn, %{new_shortcode => new_filename}) + new_shortcode = params["new_shortcode"] + new_filename = params["new_filename"] + force = params["force"] == true + + with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do + json(conn, pack.files) else - {:has_shortcode, _} -> + {:exists, _} -> conn |> put_status(:bad_request) |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - true -> + {:not_used, _} -> + conn + |> put_status(:conflict) + |> json(%{ + error: + "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" + }) + + {:loaded, _} -> conn |> put_status(:bad_request) - |> json(%{error: "new_shortcode or new_filename cannot be empty"}) + |> json(%{error: "pack \"#{name}\" is not found"}) - _ -> + {:error, :empty_values} -> conn |> put_status(:bad_request) - |> json(%{error: "new_shortcode or new_file were not specified"}) + |> json(%{error: "new_shortcode or new_filename cannot be empty"}) + + {:error, _} -> + render_error( + conn, + :internal_server_error, + "Unexpected error occurred while updating file in pack." + ) end end @@ -589,23 +358,12 @@ keeping it in cache for #{div(cache_ms, 1000)}s") neither, all the files with specific configured extenstions will be assumed to be emojis and stored in the new `pack.json` file. """ + def import_from_fs(conn, _params) do - emoji_path = emoji_dir_path() - - with {:ok, %{access: :read_write}} <- File.stat(emoji_path), - {:ok, results} <- File.ls(emoji_path) do - imported_pack_names = - results - |> Enum.filter(fn file -> - dir_path = Path.join(emoji_path, file) - # Find the directories that do NOT have pack.json - File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json")) - end) - |> Enum.map(&write_pack_json_contents/1) - - json(conn, imported_pack_names) + with {:ok, names} <- Pack.import_from_filesystem() do + json(conn, names) else - {:ok, %{access: _}} -> + {:error, :not_writable} -> conn |> put_status(:internal_server_error) |> json(%{error: "Error: emoji pack directory must be writable"}) @@ -617,44 +375,6 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end end - defp write_pack_json_contents(dir) do - dir_path = Path.join(emoji_dir_path(), dir) - emoji_txt_path = Path.join(dir_path, "emoji.txt") - - files_for_pack = files_for_pack(emoji_txt_path, dir_path) - pack_json_contents = Jason.encode!(%{pack: %{}, files: files_for_pack}) - - File.write!(Path.join(dir_path, "pack.json"), pack_json_contents) - - dir - end - - defp files_for_pack(emoji_txt_path, dir_path) do - if File.exists?(emoji_txt_path) do - # There's an emoji.txt file, it's likely from a pack installed by the pack manager. - # Make a pack.json file from the contents of that emoji.txt fileh - - # FIXME: Copy-pasted from Pleroma.Emoji/load_from_file_stream/2 - - # Create a map of shortcodes to filenames from emoji.txt - File.read!(emoji_txt_path) - |> String.split("\n") - |> Enum.map(&String.trim/1) - |> Enum.map(fn line -> - case String.split(line, ~r/,\s*/) do - # This matches both strings with and without tags - # and we don't care about tags here - [name, file | _] -> {name, file} - _ -> nil - end - end) - |> Enum.filter(fn x -> not is_nil(x) end) - |> Enum.into(%{}) - else - # If there's no emoji.txt, assume all files - # that are of certain extensions from the config are emojis and import them all - pack_extensions = Pleroma.Config.get!([:emoji, :pack_extensions]) - Pleroma.Emoji.Loader.make_shortcode_to_file_map(dir_path, pack_extensions) - end - end + defp get_filename(%Plug.Upload{filename: filename}), do: filename + defp get_filename(url) when is_binary(url), do: Path.basename(url) end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index becce3098..0fcb517cf 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -221,12 +221,13 @@ defmodule Pleroma.Web.Router do delete("/:name", EmojiAPIController, :delete) # Note: /download_from downloads and saves to instance, not to requester - post("/download_from", EmojiAPIController, :save_from) + post("/download_from", EmojiAPIController, :download_from) end # Pack info / downloading scope "/packs" do get("/", EmojiAPIController, :list_packs) + get("/:name", EmojiAPIController, :show) get("/:name/download_shared/", EmojiAPIController, :download_shared) get("/list_from", EmojiAPIController, :list_from) -- cgit v1.2.3 From ddb757f7434c7216eec1b6ba4fa8b0b7a54157c1 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 28 Mar 2020 21:15:14 +0300 Subject: emoji api packs changes in routes with docs update --- lib/pleroma/emoji/pack.ex | 26 ++-- .../controllers/emoji_api_controller.ex | 158 ++++++--------------- lib/pleroma/web/router.ex | 23 ++- 3 files changed, 64 insertions(+), 143 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 21ed12c78..eb50e52fa 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -102,9 +102,9 @@ defmodule Pleroma.Emoji.Pack do end end - @spec remove_file(String.t(), String.t()) :: + @spec delete_file(String.t(), String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} - def remove_file(name, shortcode) when byte_size(name) > 0 and byte_size(shortcode) > 0 do + def delete_file(name, shortcode) when byte_size(name) > 0 and byte_size(shortcode) > 0 do with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)}, {_, {filename, files}} when not is_nil(filename) <- {:exists, Map.pop(pack.files, shortcode)}, @@ -131,7 +131,7 @@ defmodule Pleroma.Emoji.Pack do end end - def remove_file(_, _), do: {:error, :empty_values} + def delete_file(_, _), do: {:error, :empty_values} @spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} @@ -249,8 +249,8 @@ defmodule Pleroma.Emoji.Pack do end end - @spec list_remote_packs(String.t()) :: {:ok, map()} - def list_remote_packs(url) do + @spec list_remote(String.t()) :: {:ok, map()} + def list_remote(url) do uri = url |> String.trim() @@ -269,8 +269,8 @@ defmodule Pleroma.Emoji.Pack do end end - @spec list_local_packs() :: {:ok, map()} - def list_local_packs do + @spec list_local() :: {:ok, map()} + def list_local do emoji_path = emoji_path() # Create the directory first if it does not exist. This is probably the first request made @@ -315,8 +315,8 @@ defmodule Pleroma.Emoji.Pack do end) end - @spec download(String.t()) :: {:ok, binary()} - def download(name) do + @spec get_archive(String.t()) :: {:ok, binary()} + def get_archive(name) do with {_, %__MODULE__{} = pack} <- {:exists?, load_pack(name)}, {_, true} <- {:can_download?, downloadable?(pack)} do {:ok, fetch_archive(pack)} @@ -356,15 +356,14 @@ defmodule Pleroma.Emoji.Pack do result end - @spec download_from_source(String.t(), String.t(), String.t()) :: :ok - def download_from_source(name, url, as) do + @spec download(String.t(), String.t(), String.t()) :: :ok + def download(name, url, as) do uri = url |> String.trim() |> URI.parse() with {_, true} <- {:shareable, shareable_packs_available?(uri)} do - # TODO: why do we load all packs, if we know the name of pack we need remote_pack = uri |> URI.merge("/api/pleroma/emoji/packs/#{name}") @@ -379,8 +378,7 @@ defmodule Pleroma.Emoji.Pack do {:ok, %{ sha: sha, - url: - URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/download_shared") |> to_string() + url: URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/archive") |> to_string() }} %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) -> diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 9fa857474..83a7f03e8 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -7,12 +7,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [ + :import, + :remote, + :download, :create, + :update, :delete, - :download_from, - :import_from_fs, + :add_file, :update_file, - :update_metadata + :delete_file ] ) @@ -22,14 +25,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do when action in [:download_shared, :list_packs, :list_from] ) - @doc """ - Lists packs from the remote instance. - - Since JS cannot ask remote instances for their packs due to CPS, it has to - be done by the server - """ - def list_from(conn, %{"instance_address" => address}) do - with {:ok, packs} <- Pack.list_remote_packs(address) do + def remote(conn, %{"url" => url}) do + with {:ok, packs} <- Pack.list_remote(url) do json(conn, packs) else {:shareable, _} -> @@ -39,20 +36,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - Lists the packs available on the instance as JSON. - - The information is public and does not require authentication. The format is - a map of "pack directory name" to pack.json contents. - """ - def list_packs(conn, _params) do + def list(conn, _params) do emoji_path = Path.join( Pleroma.Config.get!([:instance, :static_dir]), "emoji" ) - with {:ok, packs} <- Pack.list_local_packs() do + with {:ok, packs} <- Pack.list_local() do json(conn, packs) else {:create_dir, {:error, e}} -> @@ -87,12 +78,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - An endpoint for other instances (via admin UI) or users (via browser) - to download packs that the instance shares. - """ - def download_shared(conn, %{"name" => name}) do - with {:ok, archive} <- Pack.download(name) do + def archive(conn, %{"name" => name}) do + with {:ok, archive} <- Pack.get_archive(name) do send_download(conn, {:binary, archive}, filename: "#{name}.zip") else {:can_download?, _} -> @@ -110,15 +97,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - An admin endpoint to request downloading and storing a pack named `pack_name` from the instance - `instance_address`. - - If the requested instance's admin chose to share the pack, it will be downloaded - from that instance, otherwise it will be downloaded from the fallback source, if there is one. - """ - def download_from(conn, %{"instance_address" => address, "pack_name" => name} = params) do - with :ok <- Pack.download_from_source(name, address, params["as"]) do + def download(conn, %{"url" => url, "name" => name} = params) do + with :ok <- Pack.download(name, url, params["as"]) do json(conn, "ok") else {:shareable, _} -> @@ -138,9 +118,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - Creates an empty pack named `name` which then can be updated via the admin UI. - """ def create(conn, %{"name" => name}) do name = String.trim(name) @@ -166,9 +143,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - Deletes the pack `name` and all it's files. - """ def delete(conn, %{"name" => name}) do name = String.trim(name) @@ -192,13 +166,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - An endpoint to update `pack_names`'s metadata. - - `new_data` is the new metadata for the pack, that will replace the old metadata. - """ - def update_metadata(conn, %{"pack_name" => name, "new_data" => new_data}) do - with {:ok, pack} <- Pack.update_metadata(name, new_data) do + def update(conn, %{"name" => name, "metadata" => metadata}) do + with {:ok, pack} <- Pack.update_metadata(name, metadata) do json(conn, pack.pack) else {:has_all_files?, _} -> @@ -215,30 +184,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - @doc """ - Updates a file in a pack. - - Updating can mean three things: - - - `add` adds an emoji named `shortcode` to the pack `pack_name`, - that means that the emoji file needs to be uploaded with the request - (thus requiring it to be a multipart request) and be named `file`. - There can also be an optional `filename` that will be the new emoji file name - (if it's not there, the name will be taken from the uploaded file). - - `update` changes emoji shortcode (from `shortcode` to `new_shortcode` or moves the file - (from the current filename to `new_filename`) - - `remove` removes the emoji named `shortcode` and it's associated file - """ - - # Add - def update_file( - conn, - %{"pack_name" => pack_name, "action" => "add"} = params - ) do + def add_file(conn, %{"name" => name} = params) do filename = params["filename"] || get_filename(params["file"]) shortcode = params["shortcode"] || Path.basename(filename, Path.extname(filename)) - with {:ok, pack} <- Pack.add_file(pack_name, shortcode, filename, params["file"]) do + with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params["file"]) do json(conn, pack.files) else {:exists, _} -> @@ -249,7 +199,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do {:loaded, _} -> conn |> put_status(:bad_request) - |> json(%{error: "pack \"#{pack_name}\" is not found"}) + |> json(%{error: "pack \"#{name}\" is not found"}) {:error, :empty_values} -> conn @@ -265,13 +215,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do end end - # Remove - def update_file(conn, %{ - "pack_name" => pack_name, - "action" => "remove", - "shortcode" => shortcode - }) do - with {:ok, pack} <- Pack.remove_file(pack_name, shortcode) do + def update_file(conn, %{"name" => name, "shortcode" => shortcode} = params) do + new_shortcode = params["new_shortcode"] + new_filename = params["new_filename"] + force = params["force"] == true + + with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do json(conn, pack.files) else {:exists, _} -> @@ -279,35 +228,35 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do |> put_status(:bad_request) |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) + {:not_used, _} -> + conn + |> put_status(:conflict) + |> json(%{ + error: + "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" + }) + {:loaded, _} -> conn |> put_status(:bad_request) - |> json(%{error: "pack \"#{pack_name}\" is not found"}) + |> json(%{error: "pack \"#{name}\" is not found"}) {:error, :empty_values} -> conn |> put_status(:bad_request) - |> json(%{error: "pack name or shortcode cannot be empty"}) + |> json(%{error: "new_shortcode or new_filename cannot be empty"}) {:error, _} -> render_error( conn, :internal_server_error, - "Unexpected error occurred while removing file from pack." + "Unexpected error occurred while updating file in pack." ) end end - # Update - def update_file( - conn, - %{"pack_name" => name, "action" => "update", "shortcode" => shortcode} = params - ) do - new_shortcode = params["new_shortcode"] - new_filename = params["new_filename"] - force = params["force"] == true - - with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do + def delete_file(conn, %{"name" => name, "shortcode" => shortcode}) do + with {:ok, pack} <- Pack.delete_file(name, shortcode) do json(conn, pack.files) else {:exists, _} -> @@ -315,14 +264,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do |> put_status(:bad_request) |> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) - {:not_used, _} -> - conn - |> put_status(:conflict) - |> json(%{ - error: - "New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" - }) - {:loaded, _} -> conn |> put_status(:bad_request) @@ -331,35 +272,18 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do {:error, :empty_values} -> conn |> put_status(:bad_request) - |> json(%{error: "new_shortcode or new_filename cannot be empty"}) + |> json(%{error: "pack name or shortcode cannot be empty"}) {:error, _} -> render_error( conn, :internal_server_error, - "Unexpected error occurred while updating file in pack." + "Unexpected error occurred while removing file from pack." ) end end - def update_file(conn, %{"action" => action}) do - conn - |> put_status(:bad_request) - |> json(%{error: "Unknown action: #{action}"}) - end - - @doc """ - Imports emoji from the filesystem. - - Importing means checking all the directories in the - `$instance_static/emoji/` for directories which do not have - `pack.json`. If one has an emoji.txt file, that file will be used - to create a `pack.json` file with it's contents. If the directory has - neither, all the files with specific configured extenstions will be - assumed to be emojis and stored in the new `pack.json` file. - """ - - def import_from_fs(conn, _params) do + def import_from_filesystem(conn, _params) do with {:ok, names} <- Pack.import_from_filesystem() do json(conn, names) else diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0fcb517cf..a7e1f2f57 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -214,25 +214,24 @@ defmodule Pleroma.Web.Router do scope "/packs" do pipe_through(:admin_api) - post("/import_from_fs", EmojiAPIController, :import_from_fs) - post("/:pack_name/update_file", EmojiAPIController, :update_file) - post("/:pack_name/update_metadata", EmojiAPIController, :update_metadata) - put("/:name", EmojiAPIController, :create) + get("/import", EmojiAPIController, :import_from_filesystem) + get("/remote", EmojiAPIController, :remote) + post("/download", EmojiAPIController, :download) + + post("/:name", EmojiAPIController, :create) + patch("/:name", EmojiAPIController, :update) delete("/:name", EmojiAPIController, :delete) - # Note: /download_from downloads and saves to instance, not to requester - post("/download_from", EmojiAPIController, :download_from) + post("/:name/files", EmojiAPIController, :add_file) + patch("/:name/files", EmojiAPIController, :update_file) + delete("/:name/files", EmojiAPIController, :delete_file) end # Pack info / downloading scope "/packs" do - get("/", EmojiAPIController, :list_packs) + get("/", EmojiAPIController, :list) get("/:name", EmojiAPIController, :show) - get("/:name/download_shared/", EmojiAPIController, :download_shared) - get("/list_from", EmojiAPIController, :list_from) - - # Deprecated: POST /api/pleroma/emoji/packs/list_from (use GET instead) - post("/list_from", EmojiAPIController, :list_from) + get("/:name/archive", EmojiAPIController, :archive) end end -- cgit v1.2.3 From 36abeedf9fdd5f90c2c03a4366f8d2cc563f9386 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 30 Mar 2020 09:09:27 +0300 Subject: error rename --- lib/pleroma/emoji/pack.ex | 2 +- lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index eb50e52fa..242344374 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -188,7 +188,7 @@ defmodule Pleroma.Emoji.Pack do {:ok, names} else - {:ok, %{access: _}} -> {:error, :not_writable} + {:ok, %{access: _}} -> {:error, :no_read_write} e -> e end end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 83a7f03e8..7af9d38a1 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -287,7 +287,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do with {:ok, names} <- Pack.import_from_filesystem() do json(conn, names) else - {:error, :not_writable} -> + {:error, :no_read_write} -> conn |> put_status(:internal_server_error) |> json(%{error: "Error: emoji pack directory must be writable"}) -- cgit v1.2.3 From 4a487e4d0b744edac0d83306b80e4bd0cdef5358 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 30 Apr 2020 17:50:57 +0300 Subject: fix for auth check --- lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 7af9d38a1..d276b96a4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [ - :import, + :import_from_filesystem, :remote, :download, :create, @@ -22,7 +22,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do plug( :skip_plug, [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug] - when action in [:download_shared, :list_packs, :list_from] + when action in [:archive, :show, :list] ) def remote(conn, %{"url" => url}) do -- cgit v1.2.3 From bef34568f0d005baabca266b99ac0f6e620e6899 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Thu, 30 Apr 2020 15:02:35 +0300 Subject: Dismiss the follow request notification on rejection --- lib/pleroma/notification.ex | 10 ++++++++++ lib/pleroma/web/common_api/common_api.ex | 2 ++ 2 files changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index aaa675253..9a109dfab 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -261,6 +261,16 @@ defmodule Pleroma.Notification do |> Repo.delete_all() end + def dismiss(%Pleroma.Activity{} = activity) do + Notification + |> where([n], n.activity_id == ^activity.id) + |> Repo.delete_all() + |> case do + {_, notifications} -> {:ok, notifications} + _ -> {:error, "Cannot dismiss notification"} + end + end + def dismiss(%{id: user_id} = _user, id) do notification = Repo.get(Notification, id) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index d1efe0c36..4112e441a 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.ActivityExpiration alias Pleroma.Conversation.Participation alias Pleroma.FollowingRelationship + alias Pleroma.Notification alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -61,6 +62,7 @@ defmodule Pleroma.Web.CommonAPI do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), + {:ok, _notifications} <- Notification.dismiss(follow_activity), {:ok, _activity} <- ActivityPub.reject(%{ to: [follower.ap_id], -- cgit v1.2.3