From 4d71c4b8051d5cf54f37903091aed7f4d5c1ddd9 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Fri, 8 May 2020 12:33:01 +0300 Subject: fixed 'source' object in verify_credentials --- lib/pleroma/web/mastodon_api/views/account_view.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 420bd586f..b7cdb52b1 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -261,7 +261,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp prepare_user_bio(%User{bio: ""}), do: "" defp prepare_user_bio(%User{bio: bio}) when is_binary(bio) do - bio |> String.replace(~r(
), "\n") |> Pleroma.HTML.strip_tags() + bio + |> String.replace(~r(
), "\n") + |> Pleroma.HTML.strip_tags() + |> HtmlEntities.decode() end defp prepare_user_bio(_), do: "" -- cgit v1.2.3 From 3bde0fa3f668d42c03ce83174325920551960de3 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 11 May 2020 15:24:59 +0400 Subject: Add OpenAPI spec for TimelineController --- .../web/api_spec/operations/timeline_operation.ex | 199 +++++++++++++++++++++ .../controllers/timeline_controller.ex | 18 +- 2 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/timeline_operation.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex new file mode 100644 index 000000000..1b89035d4 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -0,0 +1,199 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.TimelineOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.Status + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def home_operation do + %Operation{ + tags: ["Timelines"], + summary: "Home timeline", + description: "View statuses from followed users", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + local_param(), + with_muted_param(), + exclude_visibilities_param(), + reply_visibility_param(), + with_relationships_param() | pagination_params() + ], + operationId: "TimelineController.home", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + } + } + end + + def direct_operation do + %Operation{ + tags: ["Timelines"], + summary: "Direct timeline", + description: + "View statuses with a “direct” privacy, from your account or in your notifications", + deprecated: true, + parameters: pagination_params(), + security: [%{"oAuth" => ["read:statuses"]}], + operationId: "TimelineController.direct", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + } + } + end + + def public_operation do + %Operation{ + tags: ["Timelines"], + summary: "Public timeline", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + local_param(), + only_media_param(), + with_muted_param(), + exclude_visibilities_param(), + reply_visibility_param(), + with_relationships_param() | pagination_params() + ], + operationId: "TimelineController.public", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()), + 401 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def hashtag_operation do + %Operation{ + tags: ["Timelines"], + summary: "Hashtag timeline", + description: "View public statuses containing the given hashtag", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + Operation.parameter( + :tag, + :path, + %Schema{type: :string}, + "Content of a #hashtag, not including # symbol.", + required: true + ), + Operation.parameter( + :any, + :query, + %Schema{type: :array, items: %Schema{type: :string}}, + "Statuses that also includes any of these tags" + ), + Operation.parameter( + :all, + :query, + %Schema{type: :array, items: %Schema{type: :string}}, + "Statuses that also includes all of these tags" + ), + Operation.parameter( + :none, + :query, + %Schema{type: :array, items: %Schema{type: :string}}, + "Statuses that do not include these tags" + ), + local_param(), + only_media_param(), + with_muted_param(), + exclude_visibilities_param(), + with_relationships_param() | pagination_params() + ], + operationId: "TimelineController.hashtag", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + } + } + end + + def list_operation do + %Operation{ + tags: ["Timelines"], + summary: "List timeline", + description: "View statuses in the given list timeline", + security: [%{"oAuth" => ["read:lists"]}], + parameters: [ + Operation.parameter( + :list_id, + :path, + %Schema{type: :string}, + "Local ID of the list in the database", + required: true + ), + with_muted_param(), + exclude_visibilities_param(), + with_relationships_param() | pagination_params() + ], + operationId: "TimelineController.list", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + } + } + end + + defp array_of_statuses do + %Schema{ + title: "ArrayOfStatuses", + type: :array, + items: Status, + example: [Status.schema().example] + } + end + + defp with_relationships_param do + Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships") + end + + defp local_param do + Operation.parameter( + :local, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Show only local statuses?" + ) + end + + defp with_muted_param do + Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") + end + + defp exclude_visibilities_param do + Operation.parameter( + :exclude_visibilities, + :query, + %Schema{type: :array, items: VisibilityScope}, + "Exclude the statuses with the given visibilities" + ) + end + + defp reply_visibility_param do + Operation.parameter( + :reply_visibility, + :query, + %Schema{type: :string, enum: ["following", "self"]}, + "Filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you." + ) + end + + defp only_media_param do + Operation.parameter( + :only_media, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Show only statuses with media attached?" + ) + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 2d67e19da..bbd576ffd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do use Pleroma.Web, :controller import Pleroma.Web.ControllerHelper, - only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1] + only: [add_link_headers: 2, add_link_headers: 3, skip_relationships?: 1] alias Pleroma.Pagination alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + plug(Pleroma.Web.ApiSpec.CastAndValidate) 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: @@ -37,10 +38,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation + # GET /api/v1/timelines/home def home(%{assigns: %{user: user}} = conn, params) do params = params + |> Map.new(fn {key, value} -> {to_string(key), value} end) |> Map.put("type", ["Create", "Announce"]) |> Map.put("blocking_user", user) |> Map.put("muting_user", user) @@ -68,6 +72,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do def direct(%{assigns: %{user: user}} = conn, params) do params = params + |> Map.new(fn {key, value} -> {to_string(key), value} end) |> Map.put("type", "Create") |> Map.put("blocking_user", user) |> Map.put("user", user) @@ -90,7 +95,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do # GET /api/v1/timelines/public def public(%{assigns: %{user: user}} = conn, params) do - local_only = truthy_param?(params["local"]) + params = Map.new(params, fn {key, value} -> {to_string(key), value} end) + + local_only = params["local"] cfg_key = if local_only do @@ -157,8 +164,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do # GET /api/v1/timelines/tag/:tag def hashtag(%{assigns: %{user: user}} = conn, params) do - local_only = truthy_param?(params["local"]) - + params = Map.new(params, fn {key, value} -> {to_string(key), value} end) + local_only = params["local"] activities = hashtag_fetching(params, user, local_only) conn @@ -172,10 +179,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do end # GET /api/v1/timelines/list/:list_id - def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do + def list(%{assigns: %{user: user}} = conn, %{list_id: id} = params) do with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do params = params + |> Map.new(fn {key, value} -> {to_string(key), value} end) |> Map.put("type", "Create") |> Map.put("blocking_user", user) |> Map.put("user", user) -- cgit v1.2.3 From 5367a00257c6f862a4a8080e0176f676ce491e4d Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 11 May 2020 15:06:23 +0200 Subject: Deletion: Handle the case of pruned objects. --- lib/pleroma/user.ex | 19 +++++++++++--- lib/pleroma/web/activity_pub/builder.ex | 10 ++++++++ .../object_validators/delete_validator.ex | 1 + lib/pleroma/web/activity_pub/transmogrifier.ex | 15 +++++++++++ lib/pleroma/web/common_api/common_api.ex | 29 ++++++++++++++++++---- 5 files changed, 66 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2a6a23fec..a86cc3202 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1554,10 +1554,23 @@ defmodule Pleroma.User do |> Stream.run() end - defp delete_activity(%{data: %{"type" => "Create", "object" => object}}, user) do - {:ok, delete_data, _} = Builder.delete(user, object) + defp delete_activity(%{data: %{"type" => "Create", "object" => object}} = activity, user) do + with {_, %Object{}} <- {:find_object, Object.get_by_ap_id(object)}, + {:ok, delete_data, _} <- Builder.delete(user, object) do + Pipeline.common_pipeline(delete_data, local: user.local) + else + {:find_object, nil} -> + # We have the create activity, but not the object, it was probably pruned. + # Insert a tombstone and try again + with {:ok, tombstone_data, _} <- Builder.tombstone(user.ap_id, object), + {:ok, _tombstone} <- Object.create(tombstone_data) do + delete_activity(activity, user) + end - Pipeline.common_pipeline(delete_data, local: user.local) + e -> + Logger.error("Could not delete #{object} created by #{activity.data["ap_id"]}") + Logger.error("Error: #{inspect(e)}") + end end defp delete_activity(%{data: %{"type" => type}} = activity, user) diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 922a444a9..4a247ad0c 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -62,6 +62,16 @@ defmodule Pleroma.Web.ActivityPub.Builder do }, []} end + @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()} + def tombstone(actor, id) do + {:ok, + %{ + "id" => id, + "actor" => actor, + "type" => "Tombstone" + }, []} + end + @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()} def like(actor, object) do with {:ok, data, meta} <- object_action(actor, object) do diff --git a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex index e06de3dff..f42c03510 100644 --- a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex @@ -51,6 +51,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do Page Question Video + Tombstone } def validate_data(cng) do cng diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index be7b57f13..921576617 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -14,7 +14,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.Types alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility @@ -720,6 +722,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ) do with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} + else + {:error, {:validate_object, _}} = e -> + # Check if we have a create activity for this + with {:ok, object_id} <- Types.ObjectID.cast(data["object"]), + %Activity{data: %{"actor" => actor}} <- + Activity.create_by_object_ap_id(object_id) |> Repo.one(), + # We have one, insert a tombstone and retry + {:ok, tombstone_data, _} <- Builder.tombstone(actor, object_id), + {:ok, _tombstone} <- Object.create(tombstone_data) do + handle_incoming(data) + else + _ -> e + end end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index c538a634f..fbef05e83 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -83,16 +83,35 @@ defmodule Pleroma.Web.CommonAPI do end def delete(activity_id, user) do - with {_, %Activity{data: %{"object" => _}} = activity} <- - {:find_activity, Activity.get_by_id_with_object(activity_id)}, - %Object{} = object <- Object.normalize(activity), + with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(activity_id)}, + {_, %Object{} = object, _} <- + {:find_object, Object.normalize(activity, false), activity}, true <- User.superuser?(user) || user.ap_id == object.data["actor"], {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do {:ok, delete} else - {:find_activity, _} -> {:error, :not_found} - _ -> {:error, dgettext("errors", "Could not delete")} + {:find_activity, _} -> + {:error, :not_found} + + {:find_object, nil, %Activity{data: %{"actor" => actor, "object" => object}}} -> + # We have the create activity, but not the object, it was probably pruned. + # Insert a tombstone and try again + with {:ok, tombstone_data, _} <- Builder.tombstone(actor, object), + {:ok, _tombstone} <- Object.create(tombstone_data) do + delete(activity_id, user) + else + _ -> + Logger.error( + "Could not insert tombstone for missing object on deletion. Object is #{object}." + ) + + {:error, dgettext("errors", "Could not delete")} + end + + _ -> + {:error, dgettext("errors", "Could not delete")} end end -- cgit v1.2.3 From e2b15e8ad31f637cbbc53b3bbc2db0972800fc88 Mon Sep 17 00:00:00 2001 From: href Date: Mon, 11 May 2020 16:28:53 +0200 Subject: Fix streamer timeout (closes #1753). Cowboy handles automatically responding to the client's ping, but doesn't automatically send a :ping frame to the client. --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 30 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index e2ffd02d0..393d093e5 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -12,8 +12,10 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do @behaviour :cowboy_websocket + # Client ping period. + @tick :timer.seconds(30) # Cowboy timeout period. - @timeout :timer.seconds(30) + @timeout :timer.seconds(60) # Hibernate every X messages @hibernate_every 100 @@ -44,7 +46,8 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do req end - {:cowboy_websocket, req, %{user: user, topic: topic, count: 0}, %{idle_timeout: @timeout}} + {:cowboy_websocket, req, %{user: user, topic: topic, count: 0, timer: nil}, + %{idle_timeout: @timeout}} else {:error, code} -> Logger.debug("#{__MODULE__} denied connection: #{inspect(code)} - #{inspect(req)}") @@ -66,11 +69,18 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do ) Streamer.add_socket(state.topic, state.user) - {:ok, state} + {:ok, %{state | timer: timer()}} + end + + # Client's Pong frame. + def websocket_handle(:pong, state) do + if state.timer, do: Process.cancel_timer(state.timer) + {:ok, %{state | timer: timer()}} end # We never receive messages. - def websocket_handle(_frame, state) do + def websocket_handle(frame, state) do + Logger.error("#{__MODULE__} received frame: #{inspect(frame)}") {:ok, state} end @@ -94,6 +104,14 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end end + # Ping tick. We don't re-queue a timer there, it is instead queued when :pong is received. + # As we hibernate there, reset the count to 0. + # If the client misses :pong, Cowboy will automatically timeout the connection after + # `@idle_timeout`. + def websocket_info(:tick, state) do + {:reply, :ping, %{state | timer: nil, count: 0}, :hibernate} + end + def terminate(reason, _req, state) do Logger.debug( "#{__MODULE__} terminating websocket connection for user #{ @@ -149,4 +167,8 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end defp expand_topic(topic, _), do: topic + + defp timer do + Process.send_after(self(), :tick, @tick) + end end -- cgit v1.2.3 From d0ba1844b031030cde5b5cf4a5714bb6ff483866 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 12 May 2020 10:52:46 +0200 Subject: ActivityPub: Fix non-federating blocks. --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +--- lib/pleroma/web/activity_pub/utils.ex | 8 ++++++-- 2 files changed, 7 insertions(+), 5 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 4955243ab..d752f4f04 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -439,7 +439,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp do_block(blocker, blocked, activity_id, local) do - outgoing_blocks = Config.get([:activitypub, :outgoing_blocks]) unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) if unfollow_blocked do @@ -447,8 +446,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if follow_activity, do: unfollow(blocker, blocked, nil, local) end - with true <- outgoing_blocks, - block_data <- make_block_data(blocker, blocked, activity_id), + with block_data <- make_block_data(blocker, blocked, activity_id), {:ok, activity} <- insert(block_data, local), _ <- notify_and_stream(activity), :ok <- maybe_federate(activity) do diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 09b80fa57..f2375bcc4 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Ecto.Changeset alias Ecto.UUID alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo @@ -169,8 +170,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do Enqueues an activity for federation if it's local """ @spec maybe_federate(any()) :: :ok - def maybe_federate(%Activity{local: true} = activity) do - if Pleroma.Config.get!([:instance, :federating]) do + def maybe_federate(%Activity{local: true, data: %{"type" => type}} = activity) do + outgoing_blocks = Config.get([:activitypub, :outgoing_blocks]) + + with true <- Config.get!([:instance, :federating]), + true <- type != "Block" || outgoing_blocks do Pleroma.Web.Federator.publish(activity) end -- cgit v1.2.3 From ca31af473c556f8320914f0621d08d59c96d3bef Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 12 May 2020 12:29:37 +0200 Subject: Transmogrifier: On incoming follow accept, update follow counts. --- lib/pleroma/web/activity_pub/transmogrifier.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 921576617..80701bb63 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -592,6 +592,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do + User.update_follower_count(followed) + User.update_following_count(follower) + ActivityPub.accept(%{ to: follow_activity.data["to"], type: "Accept", @@ -601,7 +604,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do activity_id: id }) else - _e -> :error + _e -> + :error end end -- cgit v1.2.3 From 8308611279c989819238eeadb389ef48e9a50265 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 12 May 2020 17:30:39 +0300 Subject: Startup: suggest updating OTP when the version is too low for gun I've seen quite a few people wonder what to do when presented with this error message. --- lib/pleroma/application.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index a00bc0624..9d3d92b38 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -56,7 +56,7 @@ defmodule Pleroma.Application do if (major == 22 and minor < 2) or major < 22 do raise " !!!OTP VERSION WARNING!!! - You are using gun adapter with OTP version #{version}, which doesn't support correct handling of unordered certificates chains. + You are using gun adapter with OTP version #{version}, which doesn't support correct handling of unordered certificates chains. Please update your Erlang/OTP to at least 22.2. " end else -- cgit v1.2.3 From e6d8cacf2de4ba48b881a390b0ba4582981d17a5 Mon Sep 17 00:00:00 2001 From: href Date: Tue, 12 May 2020 18:04:47 +0200 Subject: Expand and authorize streams in Streamer directly --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 69 +++++---------------- lib/pleroma/web/streamer/streamer.ex | 73 +++++++++++++++++++---- 2 files changed, 74 insertions(+), 68 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 393d093e5..94e4595d8 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -19,26 +19,12 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do # Hibernate every X messages @hibernate_every 100 - @streams [ - "public", - "public:local", - "public:media", - "public:local:media", - "user", - "user:notification", - "direct", - "list", - "hashtag" - ] - @anonymous_streams ["public", "public:local", "hashtag"] - def init(%{qs: qs} = req, state) do - with params <- :cow_qs.parse_qs(qs), + with params <- Enum.into(:cow_qs.parse_qs(qs), %{}), sec_websocket <- :cowboy_req.header("sec-websocket-protocol", req, nil), - access_token <- List.keyfind(params, "access_token", 0), - {_, stream} <- List.keyfind(params, "stream", 0), - {:ok, user} <- allow_request(stream, [access_token, sec_websocket]), - topic when is_binary(topic) <- expand_topic(stream, params) do + access_token <- Map.get(params, "access_token"), + {:ok, user} <- authenticate_request(access_token, sec_websocket), + {:ok, topic} <- Streamer.get_topic(Map.get(params, "stream"), user, params) do req = if sec_websocket do :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req) @@ -49,14 +35,14 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do {:cowboy_websocket, req, %{user: user, topic: topic, count: 0, timer: nil}, %{idle_timeout: @timeout}} else - {:error, code} -> - Logger.debug("#{__MODULE__} denied connection: #{inspect(code)} - #{inspect(req)}") - {:ok, req} = :cowboy_req.reply(code, req) + {:error, :bad_topic} -> + Logger.debug("#{__MODULE__} bad topic #{inspect(req)}") + {:ok, req} = :cowboy_req.reply(404, req) {:ok, req, state} - error -> - Logger.debug("#{__MODULE__} denied connection: #{inspect(error)} - #{inspect(req)}") - {:ok, req} = :cowboy_req.reply(400, req) + {:error, :unauthorized} -> + Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}") + {:ok, req} = :cowboy_req.reply(401, req) {:ok, req, state} end end @@ -124,50 +110,23 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end # Public streams without authentication. - defp allow_request(stream, [nil, nil]) when stream in @anonymous_streams do + defp authenticate_request(nil, nil) do {:ok, nil} end # Authenticated streams. - defp allow_request(stream, [access_token, sec_websocket]) when stream in @streams do - token = - with {"access_token", token} <- access_token do - token - else - _ -> sec_websocket - end + defp authenticate_request(access_token, sec_websocket) do + token = access_token || sec_websocket with true <- is_bitstring(token), %Token{user_id: user_id} <- Repo.get_by(Token, token: token), user = %User{} <- User.get_cached_by_id(user_id) do {:ok, user} else - _ -> {:error, 403} - end - end - - # Not authenticated. - defp allow_request(stream, _) when stream in @streams, do: {:error, 403} - - # No matching stream. - defp allow_request(_, _), do: {:error, 404} - - defp expand_topic("hashtag", params) do - case List.keyfind(params, "tag", 0) do - {_, tag} -> "hashtag:#{tag}" - _ -> nil + _ -> {:error, :unauthorized} end end - defp expand_topic("list", params) do - case List.keyfind(params, "list", 0) do - {_, list} -> "list:#{list}" - _ -> nil - end - end - - defp expand_topic(topic, _), do: topic - defp timer do Process.send_after(self(), :tick, @tick) end diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex index 5ad4aa936..49a400df7 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer/streamer.ex @@ -21,12 +21,68 @@ defmodule Pleroma.Web.Streamer do def registry, do: @registry - def add_socket(topic, %User{} = user) do - if should_env_send?(), do: Registry.register(@registry, user_topic(topic, user), true) + @public_streams ["public", "public:local", "public:media", "public:local:media"] + @user_streams ["user", "user:notification", "direct"] + + @doc "Expands and authorizes a stream, and registers the process for streaming." + @spec get_topic_and_add_socket(stream :: String.t(), User.t() | nil, Map.t() | nil) :: + {:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized} + def get_topic_and_add_socket(stream, user, params \\ %{}) do + case get_topic(stream, user, params) do + {:ok, topic} -> add_socket(topic, user) + error -> error + end + end + + @doc "Expand and authorizes a stream" + @spec get_topic(stream :: String.t(), User.t() | nil, Map.t()) :: + {:ok, topic :: String.t()} | {:error, :bad_topic} + def get_topic(stream, user, params \\ %{}) + + # Allow all public steams. + def get_topic(stream, _, _) when stream in @public_streams do + {:ok, stream} end - def add_socket(topic, _) do - if should_env_send?(), do: Registry.register(@registry, topic, false) + # Allow all hashtags streams. + def get_topic("hashtag", _, %{"tag" => tag}) do + {:ok, "hashtag:" <> tag} + end + + # Expand user streams. + def get_topic(stream, %User{} = user, _) when stream in @user_streams do + {:ok, stream <> ":" <> to_string(user.id)} + end + + def get_topic(stream, _, _) when stream in @user_streams do + {:error, :unauthorized} + end + + # List streams. + def get_topic("list", %User{} = user, %{"list" => id}) do + if Pleroma.List.get(id, user) do + {:ok, "list:" <> to_string(id)} + else + {:error, :bad_topic} + end + end + + def get_topic("list", _, _) do + {:error, :unauthorized} + end + + def get_topic(_, _, _) do + {:error, :bad_topic} + end + + @doc "Registers the process for streaming. Use `get_topic/3` to get the full authorized topic." + def add_socket(topic, user) do + if should_env_send?() do + auth? = if user, do: true + Registry.register(@registry, topic, auth?) + end + + {:ok, topic} end def remove_socket(topic) do @@ -231,13 +287,4 @@ defmodule Pleroma.Web.Streamer do true -> def should_env_send?, do: true end - - defp user_topic(topic, user) - when topic in ~w[user user:notification direct] do - "#{topic}:#{user.id}" - end - - defp user_topic(topic, _) do - topic - end end -- cgit v1.2.3 From 7803a85d2ced092fbd8e0f1bde0944bd27f8d649 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 12 May 2020 23:59:26 +0400 Subject: Add OpenAPI spec for StatusController --- lib/pleroma/bbs/handler.ex | 4 +- lib/pleroma/scheduled_activity.ex | 2 +- lib/pleroma/user/welcome_message.ex | 4 +- lib/pleroma/web/admin_api/admin_api_controller.ex | 9 +- .../web/api_spec/operations/status_operation.ex | 502 +++++++++++++++++++++ lib/pleroma/web/api_spec/schemas/status.ex | 198 ++++++-- .../web/api_spec/schemas/visibility_scope.ex | 2 +- lib/pleroma/web/common_api/activity_draft.ex | 22 +- lib/pleroma/web/common_api/common_api.ex | 29 +- lib/pleroma/web/common_api/utils.ex | 18 +- .../mastodon_api/controllers/status_controller.ex | 75 +-- lib/pleroma/web/mastodon_api/views/account_view.ex | 6 +- lib/pleroma/workers/scheduled_activity_worker.ex | 2 + 13 files changed, 757 insertions(+), 116 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/status_operation.ex (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index c7bc8ef6c..12d64c2fe 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -66,7 +66,7 @@ defmodule Pleroma.BBS.Handler do with %Activity{} <- Activity.get_by_id(activity_id), {:ok, _activity} <- - CommonAPI.post(user, %{"status" => rest, "in_reply_to_status_id" => activity_id}) do + CommonAPI.post(user, %{status: rest, in_reply_to_status_id: activity_id}) do IO.puts("Replied!") else _e -> IO.puts("Could not reply...") @@ -78,7 +78,7 @@ defmodule Pleroma.BBS.Handler do def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) - with {:ok, _activity} <- CommonAPI.post(user, %{"status" => text}) do + with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do IO.puts("Posted!") else _e -> IO.puts("Could not post...") diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 8ff06a462..0937cb7db 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -40,7 +40,7 @@ defmodule Pleroma.ScheduledActivity do %{changes: %{params: %{"media_ids" => media_ids} = params}} = changeset ) when is_list(media_ids) do - media_attachments = Utils.attachments_from_ids(%{"media_ids" => media_ids}) + media_attachments = Utils.attachments_from_ids(%{media_ids: media_ids}) params = params diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex index f0ac8ebae..f8f520285 100644 --- a/lib/pleroma/user/welcome_message.ex +++ b/lib/pleroma/user/welcome_message.ex @@ -10,8 +10,8 @@ defmodule Pleroma.User.WelcomeMessage do with %User{} = sender_user <- welcome_user(), message when is_binary(message) <- welcome_message() do CommonAPI.post(sender_user, %{ - "visibility" => "direct", - "status" => "@#{user.nickname}\n#{message}" + visibility: "direct", + status: "@#{user.nickname}\n#{message}" }) else _ -> {:ok, nil} diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 9f1fd3aeb..9821173d0 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -844,15 +844,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do + params = + params + |> Map.take(["sensitive", "visibility"]) + |> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end) + with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do - {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"]) + {:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive]) ModerationLog.insert_log(%{ action: "status_update", actor: admin, subject: activity, sensitive: sensitive, - visibility: params["visibility"] + visibility: params[:visibility] }) conn diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex new file mode 100644 index 000000000..2c28b23aa --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -0,0 +1,502 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.StatusOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus + alias Pleroma.Web.ApiSpec.Schemas.Status + alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Statuses"], + summary: "Get multiple statuses by IDs", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + Operation.parameter( + :ids, + :query, + %Schema{type: :array, items: FlakeID}, + "Array of account IDs" + ) + ], + operationId: "StatusController.index", + responses: %{ + 200 => Operation.response("Array of Status", "application/json", array_of_statuses()) + } + } + end + + def create_operation do + %Operation{ + tags: ["Statuses"], + summary: "Publish new status", + security: [%{"oAuth" => ["write:statuses"]}], + description: "Post a new status", + operationId: "StatusController.create", + requestBody: request_body("Parameters", create_request(), required: true), + responses: %{ + 200 => + Operation.response( + "Status. When `scheduled_at` is present, ScheduledStatus is returned instead", + "application/json", + %Schema{oneOf: [Status, ScheduledStatus]} + ), + 422 => Operation.response("Bad Request", "application/json", ApiError) + } + } + end + + def show_operation do + %Operation{ + tags: ["Statuses"], + summary: "View specific status", + description: "View information about a status", + operationId: "StatusController.show", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def delete_operation do + %Operation{ + tags: ["Statuses"], + summary: "Delete status", + security: [%{"oAuth" => ["write:statuses"]}], + description: "Delete one of your own statuses", + operationId: "StatusController.delete", + parameters: [id_param()], + responses: %{ + 200 => empty_object_response(), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def reblog_operation do + %Operation{ + tags: ["Statuses"], + summary: "Boost", + security: [%{"oAuth" => ["write:statuses"]}], + description: "Reshare a status", + operationId: "StatusController.reblog", + parameters: [id_param()], + requestBody: + request_body("Parameters", %Schema{ + type: :object, + properties: %{ + visibility: %Schema{allOf: [VisibilityScope], default: "public"} + } + }), + responses: %{ + 200 => status_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def unreblog_operation do + %Operation{ + tags: ["Statuses"], + summary: "Undo boost", + security: [%{"oAuth" => ["write:statuses"]}], + description: "Undo a reshare of a status", + operationId: "StatusController.unreblog", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def favourite_operation do + %Operation{ + tags: ["Statuses"], + summary: "Favourite", + security: [%{"oAuth" => ["write:favourites"]}], + description: "Add a status to your favourites list", + operationId: "StatusController.favourite", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def unfavourite_operation do + %Operation{ + tags: ["Statuses"], + summary: "Undo favourite", + security: [%{"oAuth" => ["write:favourites"]}], + description: "Remove a status from your favourites list", + operationId: "StatusController.unfavourite", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def pin_operation do + %Operation{ + tags: ["Statuses"], + summary: "Pin to profile", + security: [%{"oAuth" => ["write:accounts"]}], + description: "Feature one of your own public statuses at the top of your profile", + operationId: "StatusController.pin", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def unpin_operation do + %Operation{ + tags: ["Statuses"], + summary: "Unpin to profile", + security: [%{"oAuth" => ["write:accounts"]}], + description: "Unfeature a status from the top of your profile", + operationId: "StatusController.unpin", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def bookmark_operation do + %Operation{ + tags: ["Statuses"], + summary: "Bookmark", + security: [%{"oAuth" => ["write:bookmarks"]}], + description: "Privately bookmark a status", + operationId: "StatusController.bookmark", + parameters: [id_param()], + responses: %{ + 200 => status_response() + } + } + end + + def unbookmark_operation do + %Operation{ + tags: ["Statuses"], + summary: "Undo bookmark", + security: [%{"oAuth" => ["write:bookmarks"]}], + description: "Remove a status from your private bookmarks", + operationId: "StatusController.unbookmark", + parameters: [id_param()], + responses: %{ + 200 => status_response() + } + } + end + + def mute_conversation_operation do + %Operation{ + tags: ["Statuses"], + summary: "Mute conversation", + security: [%{"oAuth" => ["write:mutes"]}], + description: + "Do not receive notifications for the thread that this status is part of. Must be a thread in which you are a participant.", + operationId: "StatusController.mute_conversation", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def unmute_conversation_operation do + %Operation{ + tags: ["Statuses"], + summary: "Unmute conversation", + security: [%{"oAuth" => ["write:mutes"]}], + description: + "Start receiving notifications again for the thread that this status is part of", + operationId: "StatusController.unmute_conversation", + parameters: [id_param()], + responses: %{ + 200 => status_response(), + 400 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def card_operation do + %Operation{ + tags: ["Statuses"], + deprecated: true, + summary: "Preview card", + description: "Deprecated in favor of card property inlined on Status entity", + operationId: "StatusController.card", + parameters: [id_param()], + security: [%{"oAuth" => ["read:statuses"]}], + responses: %{ + 200 => + Operation.response("Card", "application/json", %Schema{ + type: :object, + nullable: true, + properties: %{ + 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} + } + }) + } + } + end + + def favourited_by_operation do + %Operation{ + tags: ["Statuses"], + summary: "Favourited by", + description: "View who favourited a given status", + operationId: "StatusController.favourited_by", + security: [%{"oAuth" => ["read:accounts"]}], + parameters: [id_param()], + responses: %{ + 200 => + Operation.response( + "Array of Accounts", + "application/json", + AccountOperation.array_of_accounts() + ), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def reblogged_by_operation do + %Operation{ + tags: ["Statuses"], + summary: "Boosted by", + description: "View who boosted a given status", + operationId: "StatusController.reblogged_by", + security: [%{"oAuth" => ["read:accounts"]}], + parameters: [id_param()], + responses: %{ + 200 => + Operation.response( + "Array of Accounts", + "application/json", + AccountOperation.array_of_accounts() + ), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def context_operation do + %Operation{ + tags: ["Statuses"], + summary: "Parent and child statuses", + description: "View statuses above and below this status in the thread", + operationId: "StatusController.context", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [id_param()], + responses: %{ + 200 => Operation.response("Context", "application/json", context()) + } + } + end + + def favourites_operation do + %Operation{ + tags: ["Statuses"], + summary: "Favourited statuses", + description: "Statuses the user has favourited", + operationId: "StatusController.favourites", + parameters: pagination_params(), + security: [%{"oAuth" => ["read:favourites"]}], + responses: %{ + 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses()) + } + } + end + + def bookmarks_operation do + %Operation{ + tags: ["Statuses"], + summary: "Bookmarked statuses", + description: "Statuses the user has bookmarked", + operationId: "StatusController.bookmarks", + parameters: [ + Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships") + | pagination_params() + ], + security: [%{"oAuth" => ["read:bookmarks"]}], + responses: %{ + 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses()) + } + } + end + + defp array_of_statuses do + %Schema{type: :array, items: Status, example: [Status.schema().example]} + end + + defp create_request do + %Schema{ + title: "StatusCreateRequest", + type: :object, + properties: %{ + status: %Schema{ + type: :string, + description: + "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided." + }, + media_ids: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: + "Array of Attachment ids to be attached as media. If provided, `status` becomes optional, and `poll` cannot be used." + }, + poll: %Schema{ + type: :object, + required: [:options], + properties: %{ + options: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: + "Array of possible answers. If provided, `media_ids` cannot be used, and `poll[expires_in]` must be provided." + }, + expires_in: %Schema{ + type: :integer, + description: + "Duration the poll should be open, in seconds. If provided, `media_ids` cannot be used, and `poll[options]` must be provided." + }, + multiple: %Schema{type: :boolean, description: "Allow multiple choices?"}, + hide_totals: %Schema{ + type: :boolean, + description: "Hide vote counts until the poll ends?" + } + } + }, + in_reply_to_id: %Schema{ + allOf: [FlakeID], + description: "ID of the status being replied to, if status is a reply" + }, + sensitive: %Schema{ + type: :boolean, + description: "Mark status and attached media as sensitive?" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field." + }, + scheduled_at: %Schema{ + type: :string, + format: :"date-time", + nullable: true, + description: + "ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future." + }, + language: %Schema{type: :string, description: "ISO 639 language code for this status."}, + # Pleroma-specific properties: + preview: %Schema{ + type: :boolean, + description: + "If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example" + }, + content_type: %Schema{ + type: :string, + description: + "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint." + }, + to: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: + "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply" + }, + visibility: %Schema{ + anyOf: [ + VisibilityScope, + %Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"} + ], + description: + "Visibility of the posted status. Besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`" + }, + expires_in: %Schema{ + type: :integer, + description: + "The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour." + }, + in_reply_to_conversation_id: %Schema{ + type: :string, + description: + "Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`." + } + }, + example: %{ + "status" => "What time is it?", + "sensitive" => "false", + "poll" => %{ + "options" => ["Cofe", "Adventure"], + "expires_in" => 420 + } + } + } + end + + defp id_param do + Operation.parameter(:id, :path, FlakeID, "Status ID", + example: "9umDrYheeY451cQnEe", + required: true + ) + end + + defp status_response do + Operation.response("Status", "application/json", Status) + end + + defp context do + %Schema{ + title: "StatusContext", + description: + "Represents the tree around a given status. Used for reconstructing threads of statuses.", + type: :object, + required: [:ancestors, :descendants], + properties: %{ + ancestors: array_of_statuses(), + descendants: array_of_statuses() + }, + example: %{ + "ancestors" => [Status.schema().example], + "descendants" => [Status.schema().example] + } + } + end +end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 2572c9641..8b87cb25b 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -19,60 +19,127 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do description: "Response schema for a status", type: :object, properties: %{ - account: Account, + account: %Schema{allOf: [Account], description: "The account that authored this status"}, application: %Schema{ + description: "The application used to post this status", type: :object, properties: %{ name: %Schema{type: :string}, website: %Schema{type: :string, nullable: true, format: :uri} } }, - bookmarked: %Schema{type: :boolean}, + bookmarked: %Schema{type: :boolean, description: "Have you bookmarked this status?"}, card: %Schema{ type: :object, nullable: true, + description: "Preview card for links included within status content", + required: [:url, :title, :description, :type], properties: %{ - 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} + type: %Schema{ + type: :string, + enum: ["link", "photo", "video", "rich"], + description: "The type of the preview card" + }, + provider_name: %Schema{ + type: :string, + nullable: true, + description: "The provider of the original resource" + }, + provider_url: %Schema{ + type: :string, + format: :uri, + description: "A link to the provider of the original resource" + }, + url: %Schema{type: :string, format: :uri, description: "Location of linked resource"}, + image: %Schema{ + type: :string, + nullable: true, + format: :uri, + description: "Preview thumbnail" + }, + title: %Schema{type: :string, description: "Title of linked resource"}, + description: %Schema{type: :string, description: "Description of preview"} } }, - content: %Schema{type: :string, format: :html}, - created_at: %Schema{type: :string, format: "date-time"}, - emojis: %Schema{type: :array, items: Emoji}, - favourited: %Schema{type: :boolean}, - favourites_count: %Schema{type: :integer}, + content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"}, + created_at: %Schema{ + type: :string, + format: "date-time", + description: "The date when this status was created" + }, + emojis: %Schema{ + type: :array, + items: Emoji, + description: "Custom emoji to be used when rendering status content" + }, + favourited: %Schema{type: :boolean, description: "Have you favourited this status?"}, + favourites_count: %Schema{ + type: :integer, + description: "How many favourites this status has received" + }, 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}, + in_reply_to_account_id: %Schema{ + allOf: [FlakeID], + nullable: true, + description: "ID of the account being replied to" + }, + in_reply_to_id: %Schema{ + allOf: [FlakeID], + nullable: true, + description: "ID of the status being replied" + }, + language: %Schema{ + type: :string, + nullable: true, + description: "Primary language of this status" + }, media_attachments: %Schema{ type: :array, - items: Attachment + items: Attachment, + description: "Media that is attached to this status" }, mentions: %Schema{ type: :array, + description: "Mentions of users within the status content", items: %Schema{ type: :object, properties: %{ - id: %Schema{type: :string}, - acct: %Schema{type: :string}, - username: %Schema{type: :string}, - url: %Schema{type: :string, format: :uri} + id: %Schema{allOf: [FlakeID], description: "The account id of the mentioned user"}, + acct: %Schema{ + type: :string, + description: + "The webfinger acct: URI of the mentioned user. Equivalent to `username` for local users, or `username@domain` for remote users." + }, + username: %Schema{type: :string, description: "The username of the mentioned user"}, + url: %Schema{ + type: :string, + format: :uri, + description: "The location of the mentioned user's profile" + } } } }, - muted: %Schema{type: :boolean}, - pinned: %Schema{type: :boolean}, + muted: %Schema{ + type: :boolean, + description: "Have you muted notifications for this status's conversation?" + }, + pinned: %Schema{ + type: :boolean, + description: "Have you pinned this status? Only appears if the status is pinnable." + }, pleroma: %Schema{ type: :object, properties: %{ - content: %Schema{type: :object, additionalProperties: %Schema{type: :string}}, - conversation_id: %Schema{type: :integer}, + content: %Schema{ + type: :object, + additionalProperties: %Schema{type: :string}, + description: + "A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`" + }, + conversation_id: %Schema{ + type: :integer, + description: "The ID of the AP context the status is associated with (if any)" + }, direct_conversation_id: %Schema{ type: :integer, nullable: true, @@ -81,6 +148,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do }, emoji_reactions: %Schema{ type: :array, + description: + "A list with emoji / reaction maps. Contains no information about the reacting users, for that use the /statuses/:id/reactions endpoint.", items: %Schema{ type: :object, properties: %{ @@ -90,27 +159,74 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do } } }, - 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} + expires_at: %Schema{ + type: :string, + format: "date-time", + nullable: true, + description: + "A datetime (ISO 8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire" + }, + in_reply_to_account_acct: %Schema{ + type: :string, + nullable: true, + description: "The `acct` property of User entity for replied user (if any)" + }, + local: %Schema{ + type: :boolean, + description: "`true` if the post was made on the local instance" + }, + spoiler_text: %Schema{ + type: :object, + additionalProperties: %Schema{type: :string}, + description: + "A map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`." + }, + thread_muted: %Schema{ + type: :boolean, + description: "`true` if the thread the post belongs to is muted" + } } }, - poll: %Schema{type: Poll, nullable: true}, + poll: %Schema{allOf: [Poll], nullable: true, description: "The poll attached to the status"}, reblog: %Schema{ allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}], - nullable: true + nullable: true, + description: "The status being reblogged" + }, + reblogged: %Schema{type: :boolean, description: "Have you boosted this status?"}, + reblogs_count: %Schema{ + type: :integer, + description: "How many boosts this status has received" + }, + replies_count: %Schema{ + type: :integer, + description: "How many replies this status has received" + }, + sensitive: %Schema{ + type: :boolean, + description: "Is this status marked as sensitive content?" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Subject or summary line, below which status content is collapsed until expanded" }, - 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: Tag}, - uri: %Schema{type: :string, format: :uri}, - url: %Schema{type: :string, nullable: true, format: :uri}, - visibility: VisibilityScope + uri: %Schema{ + type: :string, + format: :uri, + description: "URI of the status used for federation" + }, + url: %Schema{ + type: :string, + nullable: true, + format: :uri, + description: "A link to the status's HTML representation" + }, + visibility: %Schema{ + allOf: [VisibilityScope], + description: "Visibility of this status" + } }, example: %{ "account" => %{ diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex index 8c81a4d73..831734e27 100644 --- a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex +++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex @@ -9,6 +9,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do title: "VisibilityScope", description: "Status visibility", type: :string, - enum: ["public", "unlisted", "private", "direct"] + enum: ["public", "unlisted", "private", "direct", "list"] }) end diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 244cf2be5..3f1a50b96 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -58,16 +58,16 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end defp put_params(draft, params) do - params = Map.put_new(params, "in_reply_to_status_id", params["in_reply_to_id"]) + params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id]) %__MODULE__{draft | params: params} end - defp status(%{params: %{"status" => status}} = draft) do + defp status(%{params: %{status: status}} = draft) do %__MODULE__{draft | status: String.trim(status)} end defp summary(%{params: params} = draft) do - %__MODULE__{draft | summary: Map.get(params, "spoiler_text", "")} + %__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")} end defp full_payload(%{status: status, summary: summary} = draft) do @@ -84,20 +84,20 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | attachments: attachments} end - defp in_reply_to(%{params: %{"in_reply_to_status_id" => ""}} = draft), do: draft + 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 + 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 + 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"]) + in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id]) %__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation} end @@ -112,7 +112,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end defp expires_at(draft) do - case CommonAPI.check_expiry_date(draft.params["expires_in"]) do + case CommonAPI.check_expiry_date(draft.params[:expires_in]) do {:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at} {:error, message} -> add_error(draft, message) end @@ -144,7 +144,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do addressed_users = draft.mentions |> Enum.map(fn {_, mentioned_user} -> mentioned_user.ap_id end) - |> Utils.get_addressed_users(draft.params["to"]) + |> Utils.get_addressed_users(draft.params[:to]) {to, cc} = Utils.get_to_and_cc( @@ -164,7 +164,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end defp sensitive(draft) do - sensitive = draft.params["sensitive"] || Enum.member?(draft.tags, {"#nsfw", "nsfw"}) + sensitive = draft.params[:sensitive] || Enum.member?(draft.tags, {"#nsfw", "nsfw"}) %__MODULE__{draft | sensitive: sensitive} end @@ -191,7 +191,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do end defp preview?(draft) do - preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) + preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params[:preview]) %__MODULE__{draft | preview?: preview?} end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index fbef05e83..601caeb46 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -116,19 +116,18 @@ defmodule Pleroma.Web.CommonAPI do end 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 + with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do + object = Object.normalize(activity) + announce_activity = Utils.get_existing_announce(user.ap_id, object) + public = public_announce?(object, params) + if announce_activity do {:ok, announce_activity, object} else ActivityPub.announce(user, object, nil, true, public) end else - {:find_activity, _} -> {:error, :not_found} - _ -> {:error, dgettext("errors", "Could not repeat")} + _ -> {:error, :not_found} end end @@ -286,7 +285,7 @@ defmodule Pleroma.Web.CommonAPI do end end - def public_announce?(_, %{"visibility" => visibility}) + def public_announce?(_, %{visibility: visibility}) when visibility in ~w{public unlisted private direct}, do: visibility in ~w(public unlisted) @@ -296,11 +295,11 @@ defmodule Pleroma.Web.CommonAPI do def get_visibility(_, _, %Participation{}), do: {"direct", "direct"} - def get_visibility(%{"visibility" => visibility}, in_reply_to, _) + def get_visibility(%{visibility: visibility}, in_reply_to, _) when visibility in ~w{public unlisted private direct}, do: {visibility, get_replied_to_visibility(in_reply_to)} - def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to, _) do + def get_visibility(%{visibility: "list:" <> list_id}, in_reply_to, _) do visibility = {:list, String.to_integer(list_id)} {visibility, get_replied_to_visibility(in_reply_to)} end @@ -358,7 +357,7 @@ defmodule Pleroma.Web.CommonAPI do end end - def post(user, %{"status" => _} = data) do + def post(user, %{status: _} = data) do with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do draft.changes |> ActivityPub.create(draft.preview?) @@ -467,11 +466,11 @@ defmodule Pleroma.Web.CommonAPI do end end - defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do - toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)}) + defp toggle_sensitive(activity, %{sensitive: sensitive}) when sensitive in ~w(true false) do + toggle_sensitive(activity, %{sensitive: String.to_existing_atom(sensitive)}) end - defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive}) + defp toggle_sensitive(%Activity{object: object} = activity, %{sensitive: sensitive}) when is_boolean(sensitive) do new_data = Map.put(object.data, "sensitive", sensitive) @@ -485,7 +484,7 @@ defmodule Pleroma.Web.CommonAPI do defp toggle_sensitive(activity, _), do: {:ok, activity} - defp set_visibility(activity, %{"visibility" => visibility}) do + defp set_visibility(activity, %{visibility: visibility}) do Utils.update_activity_visibility(activity, visibility) end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 793f2e7f8..e8deee223 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -22,11 +22,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do require Logger require Pleroma.Constants - def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do + def attachments_from_ids(%{media_ids: ids, descriptions: desc}) do attachments_from_ids_descs(ids, desc) end - def attachments_from_ids(%{"media_ids" => ids} = _) do + def attachments_from_ids(%{media_ids: ids}) do attachments_from_ids_no_descs(ids) end @@ -37,11 +37,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do def attachments_from_ids_no_descs(ids) do Enum.map(ids, fn media_id -> case Repo.get(Object, media_id) do - %Object{data: data} = _ -> data + %Object{data: data} -> data _ -> nil end end) - |> Enum.filter(& &1) + |> Enum.reject(&is_nil/1) end def attachments_from_ids_descs([], _), do: [] @@ -51,14 +51,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do Enum.map(ids, fn media_id -> case Repo.get(Object, media_id) do - %Object{data: data} = _ -> + %Object{data: data} -> Map.put(data, "name", descs[media_id]) _ -> nil end end) - |> Enum.filter(& &1) + |> Enum.reject(&is_nil/1) end @spec get_to_and_cc( @@ -140,7 +140,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> make_poll_data() end - def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data) + def make_poll_data(%{poll: %{options: options, expires_in: expires_in}} = data) when is_list(options) do limits = Pleroma.Config.get([:instance, :poll_limits]) @@ -163,7 +163,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> DateTime.add(expires_in) |> DateTime.to_iso8601() - key = if truthy_param?(data["poll"]["multiple"]), do: "anyOf", else: "oneOf" + key = if truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf" poll = %{"type" => "Question", key => option_notes, "closed" => end_time} {:ok, {poll, emoji}} @@ -213,7 +213,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> Map.get("attachment_links", Config.get([:instance, :attachment_links])) |> truthy_param?() - content_type = get_content_type(data["content_type"]) + content_type = get_content_type(data[:content_type]) options = if visibility == "direct" && Config.get([:instance, :safe_dm_mentions]) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 12e3ba15e..25e499a77 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView + plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]) @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} @@ -97,12 +98,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.StatusOperation + @doc """ GET `/api/v1/statuses?ids[]=1&ids[]=2` `ids` query param is required """ - def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do + def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do limit = 100 activities = @@ -125,21 +128,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do Creates a scheduled status when `scheduled_at` param is present and it's far enough """ def create( - %{assigns: %{user: user}} = conn, - %{"status" => _, "scheduled_at" => scheduled_at} = params + %{ + assigns: %{user: user}, + body_params: %{status: _, scheduled_at: scheduled_at} = params + } = conn, + _ ) when not is_nil(scheduled_at) do - params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"]) + params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + + attrs = %{ + params: Map.new(params, fn {key, value} -> {to_string(key), value} end), + scheduled_at: scheduled_at + } with {:far_enough, true} <- {:far_enough, ScheduledActivity.far_enough?(scheduled_at)}, - attrs <- %{"params" => params, "scheduled_at" => scheduled_at}, {:ok, scheduled_activity} <- ScheduledActivity.create(user, attrs) do conn |> put_view(ScheduledActivityView) |> render("show.json", scheduled_activity: scheduled_activity) else {:far_enough, _} -> - create(conn, Map.drop(params, ["scheduled_at"])) + params = Map.drop(params, [:scheduled_at]) + create(%Plug.Conn{conn | body_params: params}, %{}) error -> error @@ -151,8 +162,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do Creates a regular status """ - def create(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do - params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"]) + def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do + params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) with {:ok, activity} <- CommonAPI.post(user, params) do try_render(conn, "show.json", @@ -169,12 +180,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end end - def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do - create(conn, Map.put(params, "status", "")) + def create(%{assigns: %{user: _user}, body_params: %{media_ids: _} = params} = conn, _) do + params = Map.put(params, :status, "") + create(%Plug.Conn{conn | body_params: params}, %{}) end @doc "GET /api/v1/statuses/:id" - def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def show(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "show.json", @@ -188,7 +200,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "DELETE /api/v1/statuses/:id" - def delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def delete(%{assigns: %{user: user}} = conn, %{id: id}) do with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do json(conn, %{}) else @@ -198,7 +210,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/reblog" - def reblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id} = params) do + def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params), %Activity{} = announce <- Activity.normalize(announce.data) do try_render(conn, "show.json", %{activity: announce, for: user, as: :activity}) @@ -206,7 +218,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unreblog" - def unreblog(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + def unreblog(%{assigns: %{user: user}} = conn, %{id: activity_id}) do with {:ok, _unannounce} <- CommonAPI.unrepeat(activity_id, user), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", %{activity: activity, for: user, as: :activity}) @@ -214,7 +226,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/favourite" - def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + def favourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do with {:ok, _fav} <- CommonAPI.favorite(user, activity_id), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -222,7 +234,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unfavourite" - def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + def unfavourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do with {:ok, _unfav} <- CommonAPI.unfavorite(activity_id, user), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -230,21 +242,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/pin" - def pin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do + def pin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end @doc "POST /api/v1/statuses/:id/unpin" - def unpin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do + def unpin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end @doc "POST /api/v1/statuses/:id/bookmark" - def bookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def bookmark(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), @@ -254,7 +266,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unbookmark" - def unbookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def unbookmark(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), @@ -264,7 +276,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/mute" - def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id(id), {:ok, activity} <- CommonAPI.add_mute(user, activity) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -272,7 +284,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unmute" - def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def unmute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id(id), {:ok, activity} <- CommonAPI.remove_mute(user, activity) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -281,7 +293,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do @doc "GET /api/v1/statuses/:id/card" @deprecated "https://github.com/tootsuite/mastodon/pull/11213" - def card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do + def card(%{assigns: %{user: user}} = conn, %{id: status_id}) do with %Activity{} = activity <- Activity.get_by_id(status_id), true <- Visibility.visible_for_user?(activity, user) do data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) @@ -292,7 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/favourited_by" - def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def favourited_by(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do @@ -312,7 +324,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/reblogged_by" - def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def reblogged_by(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, %Object{data: %{"announcements" => announces, "id" => ap_id}} <- @@ -344,7 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/context" - def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do + def context(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id(id) do activities = ActivityPub.fetch_activities_for_context(activity.data["context"], %{ @@ -359,11 +371,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do @doc "GET /api/v1/favourites" def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do - activities = - ActivityPub.fetch_favourites( - user, - Map.take(params, Pleroma.Pagination.page_keys()) - ) + params = + params + |> Map.new(fn {key, value} -> {to_string(key), value} end) + |> Map.take(Pleroma.Pagination.page_keys()) + + activities = ActivityPub.fetch_favourites(user, params) conn |> add_link_headers(activities) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b7cdb52b1..835dfe9f4 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -337,7 +337,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_role(data, _, _), do: data defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do - Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings) + Kernel.put_in( + data, + [:pleroma, :notification_settings], + Map.from_struct(user.notification_settings) + ) end defp maybe_put_notification_settings(data, _, _), do: data diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index 8905f4ad0..97d1efbfb 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -30,6 +30,8 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do end defp post_activity(%ScheduledActivity{user_id: user_id, params: params} = scheduled_activity) do + params = Map.new(params, fn {key, value} -> {String.to_existing_atom(key), value} end) + with {:delete, {:ok, _}} <- {:delete, ScheduledActivity.delete(scheduled_activity)}, {:user, %User{} = user} <- {:user, User.get_cached_by_id(user_id)}, {:post, {:ok, _}} <- {:post, CommonAPI.post(user, params)} do -- cgit v1.2.3 From 648cc0d72d7dbc0c906b07b3b66577c2bb6a12c6 Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:09:26 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 2c28b23aa..2b2ad04bf 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do :ids, :query, %Schema{type: :array, items: FlakeID}, - "Array of account IDs" + "Array of status IDs" ) ], operationId: "StatusController.index", -- cgit v1.2.3 From 0bb164a3e121d1453497e9203936d2284235feed Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:12:22 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 2b2ad04bf..b25d10cce 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -96,7 +96,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do tags: ["Statuses"], summary: "Boost", security: [%{"oAuth" => ["write:statuses"]}], - description: "Reshare a status", + description: "Share a status", operationId: "StatusController.reblog", parameters: [id_param()], requestBody: -- cgit v1.2.3 From 40646a7e0ed7e007e9a5f4278c44b0709a1c1754 Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:14:52 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index b25d10cce..6b0576e3c 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -222,7 +222,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do summary: "Mute conversation", security: [%{"oAuth" => ["write:mutes"]}], description: - "Do not receive notifications for the thread that this status is part of. Must be a thread in which you are a participant.", + "Do not receive notifications for the thread that this status is part of.", operationId: "StatusController.mute_conversation", parameters: [id_param()], responses: %{ -- cgit v1.2.3 From 822e9c09aaddcfc0e3dcbd2f2312e1e94a0e275d Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:23:21 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 6b0576e3c..0aacf0f3e 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -379,7 +379,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do type: :array, items: %Schema{type: :string}, description: - "Array of Attachment ids to be attached as media. If provided, `status` becomes optional, and `poll` cannot be used." + "Array of Attachment ids to be attached as media." }, poll: %Schema{ type: :object, -- cgit v1.2.3 From 5a2333925c00eb6ba9ce1c33f19389a59f19ed2c Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:23:36 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 0aacf0f3e..3b8cd40aa 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -389,7 +389,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do type: :array, items: %Schema{type: :string}, description: - "Array of possible answers. If provided, `media_ids` cannot be used, and `poll[expires_in]` must be provided." + "Array of possible answers. Must be provided with `poll[expires_in]`." }, expires_in: %Schema{ type: :integer, -- cgit v1.2.3 From cb1a3e196cc752fef4e73b0bfb4b2d02b71e0535 Mon Sep 17 00:00:00 2001 From: minibikini Date: Tue, 12 May 2020 21:23:43 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/status_operation.ex --- lib/pleroma/web/api_spec/operations/status_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 3b8cd40aa..f150b6edc 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -394,7 +394,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do expires_in: %Schema{ type: :integer, description: - "Duration the poll should be open, in seconds. If provided, `media_ids` cannot be used, and `poll[options]` must be provided." + "Duration the poll should be open, in seconds. Must be provided with `poll[options]`" }, multiple: %Schema{type: :boolean, description: "Allow multiple choices?"}, hide_totals: %Schema{ -- cgit v1.2.3 From 79ad12064dfd31f135763bae1523a94c493b6aed Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 13 May 2020 01:59:17 +0400 Subject: Fix format --- lib/pleroma/web/api_spec/operations/status_operation.ex | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index f150b6edc..a6bb87560 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -221,8 +221,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do tags: ["Statuses"], summary: "Mute conversation", security: [%{"oAuth" => ["write:mutes"]}], - description: - "Do not receive notifications for the thread that this status is part of.", + description: "Do not receive notifications for the thread that this status is part of.", operationId: "StatusController.mute_conversation", parameters: [id_param()], responses: %{ @@ -378,8 +377,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do media_ids: %Schema{ type: :array, items: %Schema{type: :string}, - description: - "Array of Attachment ids to be attached as media." + description: "Array of Attachment ids to be attached as media." }, poll: %Schema{ type: :object, @@ -388,8 +386,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do options: %Schema{ type: :array, items: %Schema{type: :string}, - description: - "Array of possible answers. Must be provided with `poll[expires_in]`." + description: "Array of possible answers. Must be provided with `poll[expires_in]`." }, expires_in: %Schema{ type: :integer, -- cgit v1.2.3 From b46811a07444187e7765f439e933f214c0a0aeb3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 12 May 2020 16:42:24 -0500 Subject: Upgrade Comeonin to v5 https://github.com/riverrun/comeonin/blob/master/UPGRADE_v5.md --- lib/pleroma/bbs/authenticator.ex | 3 +-- lib/pleroma/mfa.ex | 3 +-- lib/pleroma/plugs/authentication_plug.ex | 7 +++---- lib/pleroma/user.ex | 3 +-- lib/pleroma/web/auth/totp_authenticator.ex | 3 +-- lib/pleroma/web/mongooseim/mongoose_im_controller.ex | 3 +-- 6 files changed, 8 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex index e5b37f33e..d4494b003 100644 --- a/lib/pleroma/bbs/authenticator.ex +++ b/lib/pleroma/bbs/authenticator.ex @@ -4,7 +4,6 @@ defmodule Pleroma.BBS.Authenticator do use Sshd.PasswordAuthenticator - alias Comeonin.Pbkdf2 alias Pleroma.User def authenticate(username, password) do @@ -12,7 +11,7 @@ defmodule Pleroma.BBS.Authenticator do password = to_string(password) with %User{} = user <- User.get_by_nickname(username) do - Pbkdf2.checkpw(password, user.password_hash) + Pbkdf2.verify_pass(password, user.password_hash) else _e -> false end diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex index d353a4dad..2b77f5426 100644 --- a/lib/pleroma/mfa.ex +++ b/lib/pleroma/mfa.ex @@ -7,7 +7,6 @@ defmodule Pleroma.MFA do The MFA context. """ - alias Comeonin.Pbkdf2 alias Pleroma.User alias Pleroma.MFA.BackupCodes @@ -72,7 +71,7 @@ defmodule Pleroma.MFA do @spec generate_backup_codes(User.t()) :: {:ok, list(binary)} | {:error, String.t()} def generate_backup_codes(%User{} = user) do with codes <- BackupCodes.generate(), - hashed_codes <- Enum.map(codes, &Pbkdf2.hashpwsalt/1), + hashed_codes <- Enum.map(codes, &Pbkdf2.hash_pwd_salt/1), changeset <- Changeset.cast_backup_codes(user, hashed_codes), {:ok, _} <- User.update_and_set_cache(changeset) do {:ok, codes} diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 0061c69dc..ae4a235bd 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.AuthenticationPlug do - alias Comeonin.Pbkdf2 alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User @@ -18,7 +17,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do end def checkpw(password, "$pbkdf2" <> _ = password_hash) do - Pbkdf2.checkpw(password, password_hash) + Pbkdf2.verify_pass(password, password_hash) end def checkpw(_password, _password_hash) do @@ -37,7 +36,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do } = conn, _ ) do - if Pbkdf2.checkpw(password, password_hash) do + if Pbkdf2.verify_pass(password, password_hash) do conn |> assign(:user, auth_user) |> OAuthScopesPlug.skip_plug() @@ -47,7 +46,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do end def call(%{assigns: %{auth_credentials: %{password: _}}} = conn, _) do - Pbkdf2.dummy_checkpw() + Pbkdf2.no_user_verify() conn end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a86cc3202..cba391072 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -9,7 +9,6 @@ defmodule Pleroma.User do import Ecto.Query import Ecto, only: [assoc: 2] - alias Comeonin.Pbkdf2 alias Ecto.Multi alias Pleroma.Activity alias Pleroma.Config @@ -1926,7 +1925,7 @@ defmodule Pleroma.User do defp put_password_hash( %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset ) do - change(changeset, password_hash: Pbkdf2.hashpwsalt(password)) + change(changeset, password_hash: Pbkdf2.hash_pwd_salt(password)) end defp put_password_hash(changeset), do: changeset diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex index 98aca9a51..04e489c83 100644 --- a/lib/pleroma/web/auth/totp_authenticator.ex +++ b/lib/pleroma/web/auth/totp_authenticator.ex @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticator do - alias Comeonin.Pbkdf2 alias Pleroma.MFA alias Pleroma.MFA.TOTP alias Pleroma.User @@ -31,7 +30,7 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do code ) when is_list(codes) and is_binary(code) do - hash_code = Enum.find(codes, fn hash -> Pbkdf2.checkpw(code, hash) end) + hash_code = Enum.find(codes, fn hash -> Pbkdf2.verify_pass(code, hash) end) if hash_code do MFA.invalidate_backup_code(user, hash_code) diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index 1ed6ee521..0814b3bc3 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do use Pleroma.Web, :controller - alias Comeonin.Pbkdf2 alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User @@ -28,7 +27,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do def check_password(conn, %{"user" => username, "pass" => password}) do with %User{password_hash: password_hash, deactivated: false} <- Repo.get_by(User, nickname: username, local: true), - true <- Pbkdf2.checkpw(password, password_hash) do + true <- Pbkdf2.verify_pass(password, password_hash) do conn |> json(true) else -- cgit v1.2.3 From bcadbf964a56c071b2a3b5a5a95c419a467e5e1e Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 13 May 2020 14:15:24 +0400 Subject: Add OpenAPI spec for SuggestionController --- .../controllers/suggestion_controller.ex | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index c93a43969..f91df9ab7 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -5,11 +5,26 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug - require Logger - plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %OpenApiSpex.Operation{ + tags: ["Suggestions"], + summary: "Follow suggestions (Not implemented)", + operationId: "SuggestionController.index", + responses: %{ + 200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response() + } + } + end @doc "GET /api/v1/suggestions" def index(conn, params), -- cgit v1.2.3