diff options
44 files changed, 601 insertions, 140 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e09df22c..281f06729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance. - Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`. - The site title is now injected as a `title` tag like preloads or metadata. +- Password reset tokens now are not accepted after a certain age. <details> <summary>API Changes</summary> @@ -34,11 +35,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed +- Users with `is_discoverable` field set to false (default value) will appear in in-service search results but be hidden from external services (search bots etc.). + <details> <summary>API Changes</summary> -- Mastodon API: Current user is now included in conversation if it's the only participant. -- Mastodon API: Fixed last_status.account being not filled with account data. - + - Mastodon API: Current user is now included in conversation if it's the only participant. + - Mastodon API: Fixed last_status.account being not filled with account data. </details> ## Unreleased (Patch) @@ -48,7 +50,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`. +- Search: RUM index search speed has been fixed. - S3 Uploads with Elixir 1.11. +- Emoji Reaction activity filtering from blocked and muted accounts. - Mix task pleroma.user delete_activities for source installations. - Fix ability to update Pleroma Chat push notifications with PUT /api/v1/push/subscription and alert type pleroma:chat_mention - Forwarded reports duplication from Pleroma instances. @@ -75,7 +79,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated. - Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated. - The `discoverable` field in the `User` struct will now add a NOINDEX metatag to profile pages when false. -- Users with the `discoverable` field set to false will not show up in searches. +- Users with the `is_discoverable` field set to false will not show up in searches ([bug](https://git.pleroma.social/pleroma/pleroma/-/issues/2301)). - Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option). - Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`. - <details> diff --git a/config/config.exs b/config/config.exs index 1ac140ed0..be5257663 100644 --- a/config/config.exs +++ b/config/config.exs @@ -263,7 +263,8 @@ config :pleroma, :instance, length: 16 ] ], - show_reactions: true + show_reactions: true, + password_reset_token_validity: 60 * 60 * 24 config :pleroma, :welcome, direct_message: [ diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index 19ac6a65f..266f8cef8 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -554,7 +554,7 @@ Response: * `show_role` * `skip_thread_containment` * `fields` - * `discoverable` + * `is_discoverable` * `actor_type` * Responses: diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 843496482..6b0ad85d1 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -84,7 +84,7 @@ Has these additional fields under the `pleroma` object: - `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown - `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API -- `discoverable`: boolean, true when the user allows discovery of the account in search results and other services. +- `discoverable`: boolean, true when the user allows external services (search bots) etc. to index / list the account (regardless of this setting, user will still appear in regular search results) - `actor_type`: string, the type of this account. ## Conversations @@ -207,7 +207,7 @@ Additional parameters can be added to the JSON body/Form data: - `skip_thread_containment` - if true, skip filtering out broken threads - `allow_following_move` - if true, allows automatically follow moved following accounts - `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset. -- `discoverable` - if true, discovery of this account in search results and other services is allowed. +- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results). - `actor_type` - the type of this account. - `accepts_chat_messages` - if false, this account will reject all chat messages. diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 4d18ac30a..85551362c 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -63,6 +63,7 @@ To add configuration to your config file, you can copy it from the base config. * `external_user_synchronization`: Enabling following/followers counters synchronization for external users. * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances. * `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`). +* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day). ## Welcome * `direct_message`: - welcome message sent as a direct message. diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 3de11efce..6df1cf538 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -19,6 +19,7 @@ defmodule Mix.Pleroma do def start_pleroma do Pleroma.Config.Holder.save_default() Pleroma.Config.Oban.warn() + Pleroma.Application.limiters_setup() Application.put_env(:phoenix, :serve_endpoints, false, persistent: true) if Pleroma.Config.get(:env) != :test do diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index ceb365bb3..382c81118 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -27,7 +27,10 @@ defmodule Pleroma.Activity.Search do |> maybe_restrict_local(user) |> maybe_restrict_author(author) |> maybe_restrict_blocked(user) - |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset) + |> Pagination.fetch_paginated( + %{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum}, + :offset + ) |> maybe_fetch(user, search_query) end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 8f08a6222..ced14f87f 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -57,6 +57,7 @@ defmodule Pleroma.Application do setup_instrumenters() load_custom_modules() Pleroma.Docs.JSON.compile() + limiters_setup() adapter = Application.get_env(:tesla, :adapter) @@ -272,4 +273,10 @@ defmodule Pleroma.Application do end defp http_children(_, _), do: [] + + @spec limiters_setup() :: :ok + def limiters_setup do + [Pleroma.Web.RichMedia.Helpers, Pleroma.Web.MediaProxy] + |> Enum.each(&ConcurrentLimiter.new(&1, 1, 0)) + end end diff --git a/lib/pleroma/password_reset_token.ex b/lib/pleroma/password_reset_token.ex index 787bd4781..fea5b1c22 100644 --- a/lib/pleroma/password_reset_token.ex +++ b/lib/pleroma/password_reset_token.ex @@ -40,6 +40,7 @@ defmodule Pleroma.PasswordResetToken do @spec reset_password(binary(), map()) :: {:ok, User.t()} | {:error, binary()} def reset_password(token, data) do with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), + false <- expired?(token), %User{} = user <- User.get_cached_by_id(token.user_id), {:ok, _user} <- User.reset_password(user, data), {:ok, token} <- Repo.update(used_changeset(token)) do @@ -48,4 +49,14 @@ defmodule Pleroma.PasswordResetToken do _e -> {:error, token} end end + + def expired?(%__MODULE__{inserted_at: inserted_at}) do + validity = Pleroma.Config.get([:instance, :password_reset_token_validity], 0) + + now = NaiveDateTime.utc_now() + + difference = NaiveDateTime.diff(now, inserted_at) + + difference > validity + end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a240579f3..bcd5256c8 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -245,6 +245,18 @@ defmodule Pleroma.User do end end + def cached_blocked_users_ap_ids(user) do + Cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ -> + blocked_users_ap_ids(user) + end) + end + + def cached_muted_users_ap_ids(user) do + Cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ -> + muted_users_ap_ids(user) + end) + end + defdelegate following_count(user), to: FollowingRelationship defdelegate following(user), to: FollowingRelationship defdelegate following?(follower, followed), to: FollowingRelationship @@ -1036,6 +1048,8 @@ defmodule Pleroma.User do Cachex.del(:user_cache, "ap_id:#{user.ap_id}") Cachex.del(:user_cache, "nickname:#{user.nickname}") Cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}") + Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + Cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}") end @spec get_cached_by_ap_id(String.t()) :: User.t() | nil @@ -1342,6 +1356,8 @@ defmodule Pleroma.User do ) end + Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") + {:ok, Enum.filter([user_mute, user_notification_mute], & &1)} end end @@ -1350,6 +1366,7 @@ defmodule Pleroma.User do with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee), {:ok, user_notification_mute} <- UserRelationship.delete_notification_mute(muter, mutee) do + Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") {:ok, [user_mute, user_notification_mute]} end end @@ -2345,13 +2362,19 @@ defmodule Pleroma.User do @spec add_to_block(User.t(), User.t()) :: {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()} defp add_to_block(%User{} = user, %User{} = blocked) do - UserRelationship.create_block(user, blocked) + with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do + Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + {:ok, relationship} + end end @spec add_to_block(User.t(), User.t()) :: {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()} defp remove_from_block(%User{} = user, %User{} = blocked) do - UserRelationship.delete_block(user, blocked) + with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do + Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + {:ok, relationship} + end end def set_invisible(user, invisible) do diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index 2dab67211..f1761ef03 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -85,7 +85,6 @@ defmodule Pleroma.User.Search do |> base_query(following) |> filter_blocked_user(for_user) |> filter_invisible_users() - |> filter_discoverable_users() |> filter_internal_users() |> filter_blocked_domains(for_user) |> fts_search(query_string) @@ -163,10 +162,6 @@ defmodule Pleroma.User.Search do from(q in query, where: q.invisible == false) end - defp filter_discoverable_users(query) do - from(q in query, where: q.is_discoverable == true) - end - defp filter_internal_users(query) do from(q in query, where: q.actor_type != "Application") end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8f3ce1343..1c91bc074 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -123,7 +123,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do # Splice in the child object if we have one. activity = Maps.put_if_present(activity, :object, object) - BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id}) + ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn -> + Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) + end) {:ok, activity} else diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index 0fb05d3c4..816cc89bf 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do alias Pleroma.HTTP alias Pleroma.Web.MediaProxy - alias Pleroma.Workers.BackgroundWorker require Logger @@ -17,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do recv_timeout: 10_000 ] - def perform(:prefetch, url) do + defp prefetch(url) do # Fetching only proxiable resources if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do # If preview proxy is enabled, it'll also hit media proxy (so we're caching both requests) @@ -25,17 +24,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do Logger.debug("Prefetching #{inspect(url)} as #{inspect(prefetch_url)}") - HTTP.get(prefetch_url, [], @adapter_options) + if Pleroma.Config.get(:env) == :test do + fetch(prefetch_url) + else + ConcurrentLimiter.limit(MediaProxy, fn -> + Task.start(fn -> fetch(prefetch_url) end) + end) + end end end - def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do + defp fetch(url), do: HTTP.get(url, [], @adapter_options) + + defp preload(%{"object" => %{"attachment" => attachments}} = _message) do Enum.each(attachments, fn %{"url" => url} when is_list(url) -> url |> Enum.each(fn %{"href" => href} -> - BackgroundWorker.enqueue("media_proxy_prefetch", %{"url" => href}) + prefetch(href) x -> Logger.debug("Unhandled attachment URL object #{inspect(x)}") @@ -51,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do %{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message ) when is_list(attachments) and length(attachments) > 0 do - BackgroundWorker.enqueue("media_proxy_preload", %{"message" => message}) + preload(message) {:ok, message} end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index bbff35c36..4d8fb721e 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -24,7 +24,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer - alias Pleroma.Workers.BackgroundWorker require Logger @@ -191,7 +190,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do Object.increase_replies_count(in_reply_to) end - BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id}) + ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn -> + Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) + end) meta = meta diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 4dc45cde3..93c9f436c 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -110,6 +110,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "endpoints" => endpoints, "attachment" => fields, "tag" => emoji_tags, + # Note: key name is indeed "discoverable" (not an error) "discoverable" => user.is_discoverable, "capabilities" => capabilities } diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 451aa2477..280100c3d 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -139,6 +139,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do :query, %Schema{type: :array, items: VisibilityScope}, "Exclude visibilities" + ), + Operation.parameter( + :with_muted, + :query, + BooleanLike, + "Include reactions from muted acccounts." ) ] ++ pagination_params(), responses: %{ @@ -618,7 +624,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do allOf: [BooleanLike], nullable: true, description: - "Discovery of this account in search results and other services is allowed." + "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed." }, actor_type: ActorType }, diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex index 745d41f88..9d0e39fc7 100644 --- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex +++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex @@ -24,6 +24,12 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), Operation.parameter(:emoji, :path, :string, "Filter by a single unicode emoji", required: nil + ), + Operation.parameter( + :with_muted, + :query, + :boolean, + "Include reactions from muted acccounts." ) ], security: [%{"oAuth" => ["read:statuses"]}], diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index b3b6ceb68..4ab918d83 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -31,6 +31,12 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do :query, %Schema{type: :array, items: FlakeID}, "Array of status IDs" + ), + Operation.parameter( + :with_muted, + :query, + BooleanLike, + "Include reactions from muted acccounts." ) ], operationId: "StatusController.index", @@ -67,7 +73,15 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do description: "View information about a status", operationId: "StatusController.show", security: [%{"oAuth" => ["read:statuses"]}], - parameters: [id_param()], + parameters: [ + id_param(), + Operation.parameter( + :with_muted, + :query, + BooleanLike, + "Include reactions from muted acccounts." + ) + ], responses: %{ 200 => status_response(), 404 => Operation.response("Not Found", "application/json", ApiError) diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index ca79f0747..684f6fc92 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -127,7 +127,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do discoverable: %Schema{ type: :boolean, description: - "whether the user allows discovery of the account in search results and other services." + "whether the user allows indexing / listing of the account by external services (search engines etc.)." }, no_rich_text: %Schema{ type: :boolean, diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 784fdc975..7011b7eb1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -208,7 +208,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do if bot, do: {:ok, "Service"}, else: {:ok, "Person"} end) |> Maps.put_if_present(:actor_type, params[:actor_type]) + # Note: param name is indeed :locked (not an error) |> Maps.put_if_present(:is_locked, params[:locked]) + # Note: param name is indeed :discoverable (not an error) |> Maps.put_if_present(:is_discoverable, params[:discoverable]) # What happens here: @@ -292,7 +294,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> render("index.json", activities: activities, for: reading_user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) else error -> user_visibility_error(conn, error) diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 4d9be5240..9e3a584f0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -109,7 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do `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 = @@ -121,7 +121,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do render(conn, "index.json", activities: activities, for: user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) end @@ -189,13 +190,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id" - def show(%{assigns: %{user: user}} = conn, %{id: id}) do + def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "show.json", activity: activity, for: user, - with_direct_conversation_id: true + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) ) else _ -> {:error, :not_found} diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index ac96520a3..852bd0695 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -62,7 +62,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> render("index.json", activities: activities, for: user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) end @@ -119,7 +120,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> render("index.json", activities: activities, for: user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) end end @@ -173,7 +175,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> render("index.json", activities: activities, for: user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) end end @@ -202,7 +205,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do render(conn, "index.json", activities: activities, for: user, - as: :activity + as: :activity, + with_muted: Map.get(params, :with_muted, false) ) else _e -> render_error(conn, :forbidden, "Error.") diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 7cbbd3750..2301e21cf 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do alias Pleroma.Web.MastodonAPI.PollView alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MediaProxy + alias Pleroma.Web.PleromaAPI.EmojiReactionController import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2] @@ -294,21 +295,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end emoji_reactions = - with %{data: %{"reactions" => emoji_reactions}} <- object do - Enum.map(emoji_reactions, fn - [emoji, users] when is_list(users) -> - build_emoji_map(emoji, users, opts[:for]) - - {emoji, users} when is_list(users) -> - build_emoji_map(emoji, users, opts[:for]) - - _ -> - nil - end) - |> Enum.reject(&is_nil/1) - else - _ -> [] - end + object.data + |> Map.get("reactions", []) + |> EmojiReactionController.filter_allowed_users( + opts[:for], + Map.get(opts, :with_muted, false) + ) + |> Stream.map(fn {emoji, users} -> + build_emoji_map(emoji, users, opts[:for]) + end) + |> Enum.to_list() # Status muted state (would do 1 request per status unless user mutes are preloaded) muted = diff --git a/lib/pleroma/web/metadata/providers/restrict_indexing.ex b/lib/pleroma/web/metadata/providers/restrict_indexing.ex index 900c2434d..a08a04b4a 100644 --- a/lib/pleroma/web/metadata/providers/restrict_indexing.ex +++ b/lib/pleroma/web/metadata/providers/restrict_indexing.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do @behaviour Pleroma.Web.Metadata.Providers.Provider @moduledoc """ - Restricts indexing of remote users. + Restricts indexing of remote and/or non-discoverable users. """ @impl true diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index 77564b342..bfc0a1f19 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -140,8 +140,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do exclude_users = - User.blocked_users_ap_ids(user) ++ - if params[:with_muted], do: [], else: User.muted_users_ap_ids(user) + User.cached_blocked_users_ap_ids(user) ++ + if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user) chats = user_id diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex index ae199a50f..dd9c746dc 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.Plugs.OAuthScopesPlug @@ -29,13 +30,42 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), %Object{data: %{"reactions" => reactions}} when is_list(reactions) <- Object.normalize(activity) do - reactions = filter(reactions, params) + reactions = + reactions + |> filter(params) + |> filter_allowed_users(user, Map.get(params, :with_muted, false)) + render(conn, "index.json", emoji_reactions: reactions, user: user) else _e -> json(conn, []) end end + def filter_allowed_users(reactions, user, with_muted) do + exclude_ap_ids = + if is_nil(user) do + [] + else + User.cached_blocked_users_ap_ids(user) ++ + if not with_muted, do: User.cached_muted_users_ap_ids(user), else: [] + end + + filter_emoji = fn emoji, users -> + case Enum.reject(users, &(&1 in exclude_ap_ids)) do + [] -> nil + users -> {emoji, users} + end + end + + reactions + |> Stream.map(fn + [emoji, users] when is_list(users) -> filter_emoji.(emoji, users) + {emoji, users} when is_list(users) -> filter_emoji.(emoji, users) + _ -> nil + end) + |> Stream.reject(&is_nil/1) + end + defp filter(reactions, %{emoji: emoji}) when is_binary(emoji) do Enum.filter(reactions, fn [e, _] -> e == emoji end) end diff --git a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex index e0f98b50a..110e8a041 100644 --- a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex +++ b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do render_many(emoji_reactions, __MODULE__, "show.json", opts) end - def render("show.json", %{emoji_reaction: [emoji, user_ap_ids], user: user}) do + def render("show.json", %{emoji_reaction: {emoji, user_ap_ids}, user: user}) do users = fetch_users(user_ap_ids) %{ diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index d67b594b5..442bf9995 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -78,11 +78,6 @@ defmodule Pleroma.Web.RichMedia.Helpers do def fetch_data_for_activity(_), do: %{} - def perform(:fetch, %Activity{} = activity) do - fetch_data_for_activity(activity) - :ok - end - def rich_media_get(url) do headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}] diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex index 800ab8954..b1a9d810e 100644 --- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex @@ -17,6 +17,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do def reset(conn, %{"token" => token}) do with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), + false <- PasswordResetToken.expired?(token), %User{} = user <- User.get_cached_by_id(token.user_id) do render(conn, "reset.html", %{ token: token, diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 55b5a13d9..0647c65ae 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -3,9 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackgroundWorker do - alias Pleroma.Activity alias Pleroma.User - alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy use Pleroma.Workers.WorkerHelper, queue: "background" @@ -32,19 +30,6 @@ defmodule Pleroma.Workers.BackgroundWorker do {:ok, User.Import.perform(String.to_atom(op), user, identifiers)} end - def perform(%Job{args: %{"op" => "media_proxy_preload", "message" => message}}) do - MediaProxyWarmingPolicy.perform(:preload, message) - end - - def perform(%Job{args: %{"op" => "media_proxy_prefetch", "url" => url}}) do - MediaProxyWarmingPolicy.perform(:prefetch, url) - end - - def perform(%Job{args: %{"op" => "fetch_data_for_activity", "activity_id" => activity_id}}) do - activity = Activity.get_by_id(activity_id) - Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity) - end - def perform(%Job{ args: %{"op" => "move_following", "origin_id" => origin_id, "target_id" => target_id} }) do diff --git a/priv/repo/migrations/20200915095704_remove_background_jobs.exs b/priv/repo/migrations/20200915095704_remove_background_jobs.exs new file mode 100644 index 000000000..9785bfb8a --- /dev/null +++ b/priv/repo/migrations/20200915095704_remove_background_jobs.exs @@ -0,0 +1,22 @@ +defmodule Pleroma.Repo.Migrations.RemoveBackgroundJobs do + use Ecto.Migration + + import Ecto.Query, only: [from: 2] + + def up do + from(j in "oban_jobs", + where: + j.queue == ^"background" and + fragment("?->>'op'", j.args) in ^[ + "fetch_data_for_activity", + "media_proxy_prefetch", + "media_proxy_preload" + ] and + j.worker == ^"Pleroma.Workers.BackgroundWorker", + select: [:id] + ) + |> Pleroma.Repo.delete_all() + end + + def down, do: :ok +end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 0cfed4555..f52629f8a 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -12,7 +12,7 @@ defmodule Pleroma.Config.DeprecationWarningsTest do alias Pleroma.Config.DeprecationWarnings test "check_old_mrf_config/0" do - clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy) + clear_config([:instance, :rewrite_policy], []) clear_config([:instance, :mrf_transparency], true) clear_config([:instance, :mrf_transparency_exclusions], []) diff --git a/test/pleroma/user_search_test.exs b/test/pleroma/user_search_test.exs index 31d787ffa..de1df2e9c 100644 --- a/test/pleroma/user_search_test.exs +++ b/test/pleroma/user_search_test.exs @@ -65,12 +65,13 @@ defmodule Pleroma.UserSearchTest do assert found_user.id == user.id end - test "excludes users when discoverable is false" do + # Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability + test "includes non-discoverable users in results" do insert(:user, %{nickname: "john 3000", is_discoverable: false}) insert(:user, %{nickname: "john 3001"}) users = User.search("john") - assert Enum.count(users) == 1 + assert Enum.count(users) == 2 end test "excludes service actors from results" do diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs index 1710c4d2a..84362ce78 100644 --- a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs @@ -3,10 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do - use Pleroma.DataCase + use ExUnit.Case + use Pleroma.Tests.Helpers alias Pleroma.HTTP - alias Pleroma.Tests.ObanHelpers alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy import Mock @@ -25,13 +25,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do setup do: clear_config([:media_proxy, :enabled], true) test "it prefetches media proxy URIs" do + Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} -> + {:ok, %Tesla.Env{status: 200, body: ""}} + end) + with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do MediaProxyWarmingPolicy.filter(@message) - ObanHelpers.perform_all() - # Performing jobs which has been just enqueued - ObanHelpers.perform_all() - assert called(HTTP.get(:_, :_, :_)) end end diff --git a/test/pleroma/web/admin_api/search_test.exs b/test/pleroma/web/admin_api/search_test.exs index 92a116c65..9bc58640c 100644 --- a/test/pleroma/web/admin_api/search_test.exs +++ b/test/pleroma/web/admin_api/search_test.exs @@ -203,6 +203,7 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do assert count == 1 end + # Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability test "it returns non-discoverable users" do insert(:user) insert(:user, is_discoverable: false) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 58ce76ab8..e8a00dd6b 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -436,6 +436,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct") assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200) end + + test "muted reactions", %{user: user, conn: conn} do + user2 = insert(:user) + User.mute(user, user2) + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user2, "🎅") + + result = + conn + |> get("/api/v1/accounts/#{user.id}/statuses") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> get("/api/v1/accounts/#{user.id}/statuses?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end end defp local_and_remote_activities(%{local: local, remote: remote}) do diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index d95200f99..30d542dfa 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -328,7 +328,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "posting a status with OGP link preview", %{conn: conn} do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) clear_config([:rich_media, :enabled], true) conn = @@ -1197,7 +1197,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "returns rich-media card", %{conn: conn, user: user} do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp"}) @@ -1242,7 +1242,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "replaces missing description with an empty string", %{conn: conn, user: user} do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp-missing-data"}) @@ -1759,4 +1759,75 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id) end + + describe "muted reactions" do + test "index" do + %{conn: conn, user: user} = oauth_access(["read:statuses"]) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + User.mute(user, other_user) + + result = + conn + |> get("/api/v1/statuses/?ids[]=#{activity.id}") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> get("/api/v1/statuses/?ids[]=#{activity.id}&with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end + + test "show" do + # %{conn: conn, user: user, token: token} = oauth_access(["read:statuses"]) + %{conn: conn, user: user, token: _token} = oauth_access(["read:statuses"]) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + User.mute(user, other_user) + + result = + conn + |> get("/api/v1/statuses/#{activity.id}") + |> json_response_and_validate_schema(200) + + assert %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } = result + + result = + conn + |> get("/api/v1/statuses/#{activity.id}?with_muted=true") + |> json_response_and_validate_schema(200) + + assert %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } = result + end + end end diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index 4c08ad60a..8356b64d3 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -54,6 +54,42 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert private_activity.id in status_ids refute direct_activity.id in status_ids end + + test "muted emotions", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + User.mute(user, other_user) + + result = + conn + |> assign(:user, user) + |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/timelines/home?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end end describe "public" do @@ -159,6 +195,48 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert length(json_response_and_validate_schema(conn, :ok)) == 1 end + + test "muted emotions", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) + + conn = + conn + |> assign(:user, user) + |> assign(:token, token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + User.mute(user, other_user) + + result = + conn + |> get("/api/v1/timelines/public") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> get("/api/v1/timelines/public?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end end defp local_and_remote_activities do @@ -428,6 +506,44 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert id == to_string(activity_one.id) end + + test "muted emotions", %{user: user, conn: conn} do + user2 = insert(:user) + user3 = insert(:user) + {:ok, activity} = CommonAPI.post(user2, %{status: "."}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user3, "🎅") + User.mute(user, user3) + + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, user2) + + result = + conn + |> get("/api/v1/timelines/list/#{list.id}") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> get("/api/v1/timelines/list/#{list.id}?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end end describe "hashtag" do @@ -476,6 +592,48 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert [status_none] == json_response_and_validate_schema(all_test, :ok) end + + test "muted emotions", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) + + conn = + conn + |> assign(:user, user) + |> assign(:token, token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "test #2hu"}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + User.mute(user, other_user) + + result = + conn + |> get("/api/v1/timelines/tag/2hu") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [] + } + } + ] = result + + result = + conn + |> get("/api/v1/timelines/tag/2hu?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [ + %{ + "pleroma" => %{ + "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}] + } + } + ] = result + end end describe "hashtag timeline handling of :restrict_unauthenticated setting" do diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index 665199f97..f2a7469ed 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -73,6 +73,50 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do ] end + test "doesn't show reactions from muted and blocked users" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"}) + + {:ok, _} = User.mute(user, other_user) + {:ok, _} = User.block(other_user, third_user) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + activity = Repo.get(Activity, activity.id) + status = StatusView.render("show.json", activity: activity) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 1, me: false} + ] + + status = StatusView.render("show.json", activity: activity, for: user) + + assert status[:pleroma][:emoji_reactions] == [] + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "☕") + + status = StatusView.render("show.json", activity: activity) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 2, me: false} + ] + + status = StatusView.render("show.json", activity: activity, for: user) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 1, me: false} + ] + + status = StatusView.render("show.json", activity: activity, for: other_user) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 1, me: true} + ] + end + test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do user = insert(:user) diff --git a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs index 282d132c8..52399fdc8 100644 --- a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs +++ b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs @@ -18,7 +18,7 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do }) == [] end - test "for local user when discoverable is false" do + test "for local user when `is_discoverable` is false" do assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ user: %Pleroma.User{local: true, is_discoverable: false} }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] diff --git a/test/pleroma/web/metadata_test.exs b/test/pleroma/web/metadata_test.exs deleted file mode 100644 index 8fb946540..000000000 --- a/test/pleroma/web/metadata_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MetadataTest do - use Pleroma.DataCase, async: true - - import Pleroma.Factory - - describe "restrict indexing remote users" do - test "for remote user" do - user = insert(:user, local: false) - - assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - - test "for local user" do - user = insert(:user, is_discoverable: false) - - assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - - test "for local user set to discoverable" do - user = insert(:user, is_discoverable: true) - - refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - end - - describe "no metadata for private instances" do - test "for local user set to discoverable" do - clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", is_discoverable: true) - - assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) - end - - test "search exclusion metadata is included" do - clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", is_discoverable: false) - - assert ~s(<meta content="noindex, noarchive" name="robots">) == - Pleroma.Web.Metadata.build_tags(%{user: user}) - end - end -end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs index 3deab30d1..bda9c20c6 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -106,6 +106,48 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do result end + test "GET /api/v1/pleroma/statuses/:id/reactions?with_muted=true", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + + token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user2, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user3, "🎅") + + result = + conn + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert [%{"name" => "🎅", "count" => 2}] = result + + User.mute(user, user3) + + result = + conn + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert [%{"name" => "🎅", "count" => 1}] = result + + result = + conn + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions?with_muted=true") + |> json_response_and_validate_schema(200) + + assert [%{"name" => "🎅", "count" => 2}] = result + end + test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do clear_config([:instance, :show_reactions], false) diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs index ae8257870..93eef00a2 100644 --- a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs @@ -48,7 +48,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do clear_config([:rich_media, :enabled], true) - Tesla.Mock.mock(fn + Tesla.Mock.mock_global(fn %{url: "https://example.com/ogp"} -> %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")} end) diff --git a/test/pleroma/web/twitter_api/password_controller_test.exs b/test/pleroma/web/twitter_api/password_controller_test.exs index a5e9e2178..6d08075cc 100644 --- a/test/pleroma/web/twitter_api/password_controller_test.exs +++ b/test/pleroma/web/twitter_api/password_controller_test.exs @@ -31,9 +31,48 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do assert response =~ "<h2>Password Reset for #{user.nickname}</h2>" end + + test "it returns an error when the token has expired", %{conn: conn} do + clear_config([:instance, :password_reset_token_validity], 0) + + user = insert(:user) + {:ok, token} = PasswordResetToken.create_token(user) + + :timer.sleep(2000) + + response = + conn + |> get("/api/pleroma/password_reset/#{token.token}") + |> html_response(:ok) + + assert response =~ "<h2>Invalid Token</h2>" + end end describe "POST /api/pleroma/password_reset" do + test "it fails for an expired token", %{conn: conn} do + clear_config([:instance, :password_reset_token_validity], 0) + + user = insert(:user) + {:ok, token} = PasswordResetToken.create_token(user) + :timer.sleep(2000) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) + + params = %{ + "password" => "test", + password_confirmation: "test", + token: token.token + } + + response = + conn + |> assign(:user, user) + |> post("/api/pleroma/password_reset", %{data: params}) + |> html_response(:ok) + + refute response =~ "<h2>Password changed!</h2>" + end + test "it returns HTTP 200", %{conn: conn} do user = insert(:user) {:ok, token} = PasswordResetToken.create_token(user) |