summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/config.ex2
-rw-r--r--lib/pleroma/application.ex3
-rw-r--r--lib/pleroma/conversation/participation.ex28
-rw-r--r--lib/pleroma/docs/json.ex2
-rw-r--r--lib/pleroma/moderation_log.ex11
-rw-r--r--lib/pleroma/object/containment.ex6
-rw-r--r--lib/pleroma/object/fetcher.ex5
-rw-r--r--lib/pleroma/plugs/rate_limiter.ex131
-rw-r--r--lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex44
-rw-r--r--lib/pleroma/plugs/rate_limiter/rate_limiter.ex227
-rw-r--r--lib/pleroma/plugs/rate_limiter/supervisor.ex16
-rw-r--r--lib/pleroma/plugs/trailing_format_plug.ex3
-rw-r--r--lib/pleroma/user/search.ex12
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex1
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex12
-rw-r--r--lib/pleroma/web/endpoint.ex2
-rw-r--r--lib/pleroma/web/feed/feed_controller.ex21
-rw-r--r--lib/pleroma/web/feed/feed_view.ex35
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/auth_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/search_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex6
-rw-r--r--lib/pleroma/web/mongooseim/mongoose_im_controller.ex4
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex1
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex3
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex5
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/account_controller.ex2
-rw-r--r--lib/pleroma/web/rel_me.ex14
-rw-r--r--lib/pleroma/web/router.ex2
-rw-r--r--lib/pleroma/web/templates/feed/feed/_activity.xml.eex8
-rw-r--r--lib/pleroma/web/templates/feed/feed/feed.xml.eex2
34 files changed, 420 insertions, 212 deletions
diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex
index 11e4fde43..0e21408b2 100644
--- a/lib/mix/tasks/pleroma/config.ex
+++ b/lib/mix/tasks/pleroma/config.ex
@@ -45,7 +45,7 @@ defmodule Mix.Tasks.Pleroma.Config do
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
config_path = "config/#{env}.exported_from_db.secret.exs"
- {:ok, file} = File.open(config_path, [:write])
+ {:ok, file} = File.open(config_path, [:write, :utf8])
IO.write(file, "use Mix.Config\r\n")
Repo.all(Config)
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index d681eecc8..2b6a55f98 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -36,7 +36,8 @@ defmodule Pleroma.Application do
Pleroma.Emoji,
Pleroma.Captcha,
Pleroma.Daemons.ScheduledActivityDaemon,
- Pleroma.Daemons.ActivityExpirationDaemon
+ Pleroma.Daemons.ActivityExpirationDaemon,
+ Pleroma.Plugs.RateLimiter.Supervisor
] ++
cachex_children() ++
hackney_pool_children() ++
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 176b82a20..aafe57280 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -122,9 +122,37 @@ defmodule Pleroma.Conversation.Participation do
order_by: [desc: p.updated_at],
preload: [conversation: [:users]]
)
+ |> restrict_recipients(user, params)
|> Pleroma.Pagination.fetch_paginated(params)
end
+ def restrict_recipients(query, user, %{"recipients" => user_ids}) do
+ user_ids =
+ [user.id | user_ids]
+ |> Enum.uniq()
+ |> Enum.reduce([], fn user_id, acc ->
+ case FlakeId.Ecto.CompatType.dump(user_id) do
+ {:ok, user_id} -> [user_id | acc]
+ _ -> acc
+ end
+ end)
+
+ conversation_subquery =
+ __MODULE__
+ |> group_by([p], p.conversation_id)
+ |> having(
+ [p],
+ count(p.user_id) == ^length(user_ids) and
+ fragment("array_agg(?) @> ?", p.user_id, ^user_ids)
+ )
+ |> select([p], %{id: p.conversation_id})
+
+ query
+ |> join(:inner, [p], c in subquery(conversation_subquery), on: p.conversation_id == c.id)
+ end
+
+ def restrict_recipients(query, _, _), do: query
+
def for_user_and_conversation(user, conversation) do
from(p in __MODULE__,
where: p.user_id == ^user.id,
diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex
index 18ba01d58..f2a56d845 100644
--- a/lib/pleroma/docs/json.ex
+++ b/lib/pleroma/docs/json.ex
@@ -5,7 +5,7 @@ defmodule Pleroma.Docs.JSON do
def process(descriptions) do
config_path = "docs/generate_config.json"
- with {:ok, file} <- File.open(config_path, [:write]),
+ with {:ok, file} <- File.open(config_path, [:write, :utf8]),
json <- generate_json(descriptions),
:ok <- IO.write(file, json),
:ok <- File.close(file) do
diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex
index 9dc4a94c9..ffa5dc25d 100644
--- a/lib/pleroma/moderation_log.ex
+++ b/lib/pleroma/moderation_log.ex
@@ -616,6 +616,17 @@ defmodule Pleroma.ModerationLog do
"@#{actor_nickname} deleted status ##{subject_id}"
end
+ @spec get_log_entry_message(ModerationLog) :: String.t()
+ def get_log_entry_message(%ModerationLog{
+ data: %{
+ "actor" => %{"nickname" => actor_nickname},
+ "action" => "force_password_reset",
+ "subject" => subjects
+ }
+ }) do
+ "@#{actor_nickname} force password reset for users: #{users_to_nicknames_string(subjects)}"
+ end
+
defp nicknames_to_string(nicknames) do
nicknames
|> Enum.map(&"@#{&1}")
diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex
index 68535c09e..a1f9c1250 100644
--- a/lib/pleroma/object/containment.ex
+++ b/lib/pleroma/object/containment.ex
@@ -64,15 +64,15 @@ defmodule Pleroma.Object.Containment do
def contain_origin(id, %{"attributedTo" => actor} = params),
do: contain_origin(id, Map.put(params, "actor", actor))
- def contain_origin_from_id(_id, %{"id" => nil}), do: :error
-
- def contain_origin_from_id(id, %{"id" => other_id} = _params) do
+ def contain_origin_from_id(id, %{"id" => other_id} = _params) when is_binary(other_id) do
id_uri = URI.parse(id)
other_uri = URI.parse(other_id)
compare_uris(id_uri, other_uri)
end
+ def contain_origin_from_id(_id, _data), do: :error
+
def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}),
do: contain_origin(id, object)
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index 441ae8b65..9a9a46550 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -38,7 +38,8 @@ defmodule Pleroma.Object.Fetcher do
data <- maybe_reinject_internal_fields(data, struct),
changeset <- Object.change(struct, %{data: data}),
changeset <- touch_changeset(changeset),
- {:ok, object} <- Repo.insert_or_update(changeset) do
+ {:ok, object} <- Repo.insert_or_update(changeset),
+ {:ok, object} <- Object.set_cache(object) do
{:ok, object}
else
e ->
@@ -53,7 +54,7 @@ defmodule Pleroma.Object.Fetcher do
{:ok, object} <- reinject_object(object, data) do
{:ok, object}
else
- {:local, true} -> object
+ {:local, true} -> {:ok, object}
e -> {:error, e}
end
end
diff --git a/lib/pleroma/plugs/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter.ex
deleted file mode 100644
index 31388f574..000000000
--- a/lib/pleroma/plugs/rate_limiter.ex
+++ /dev/null
@@ -1,131 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.RateLimiter do
- @moduledoc """
-
- ## Configuration
-
- A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
-
- * The first element: `scale` (Integer). The time scale in milliseconds.
- * The second element: `limit` (Integer). How many requests to limit in the time scale provided.
-
- It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
-
- To disable a limiter set its value to `nil`.
-
- ### Example
-
- config :pleroma, :rate_limit,
- one: {1000, 10},
- two: [{10_000, 10}, {10_000, 50}],
- foobar: nil
-
- Here we have three limiters:
-
- * `one` which is not over 10req/1s
- * `two` which has two limits: 10req/10s for unauthenticated users and 50req/10s for authenticated users
- * `foobar` which is disabled
-
- ## Usage
-
- AllowedSyntax:
-
- plug(Pleroma.Plugs.RateLimiter, :limiter_name)
- plug(Pleroma.Plugs.RateLimiter, {:limiter_name, options})
-
- Allowed options:
-
- * `bucket_name` overrides bucket name (e.g. to have a separate limit for a set of actions)
- * `params` appends values of specified request params (e.g. ["id"]) to bucket name
-
- Inside a controller:
-
- plug(Pleroma.Plugs.RateLimiter, :one when action == :one)
- plug(Pleroma.Plugs.RateLimiter, :two when action in [:two, :three])
-
- plug(
- Pleroma.Plugs.RateLimiter,
- {:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
- when action in ~w(fav_status unfav_status)a
- )
-
- or inside a router pipeline:
-
- pipeline :api do
- ...
- plug(Pleroma.Plugs.RateLimiter, :one)
- ...
- end
- """
- import Pleroma.Web.TranslationHelpers
- import Plug.Conn
-
- alias Pleroma.User
-
- def init(limiter_name) when is_atom(limiter_name) do
- init({limiter_name, []})
- end
-
- def init({limiter_name, opts}) do
- case Pleroma.Config.get([:rate_limit, limiter_name]) do
- nil -> nil
- config -> {limiter_name, config, opts}
- end
- end
-
- # Do not limit if there is no limiter configuration
- def call(conn, nil), do: conn
-
- def call(conn, settings) do
- case check_rate(conn, settings) do
- {:ok, _count} ->
- conn
-
- {:error, _count} ->
- render_throttled_error(conn)
- end
- end
-
- defp bucket_name(conn, limiter_name, opts) do
- bucket_name = opts[:bucket_name] || limiter_name
-
- if params_names = opts[:params] do
- params_values = for p <- Enum.sort(params_names), do: conn.params[p]
- Enum.join([bucket_name] ++ params_values, ":")
- else
- bucket_name
- end
- end
-
- defp check_rate(
- %{assigns: %{user: %User{id: user_id}}} = conn,
- {limiter_name, [_, {scale, limit}], opts}
- ) do
- bucket_name = bucket_name(conn, limiter_name, opts)
- ExRated.check_rate("#{bucket_name}:#{user_id}", scale, limit)
- end
-
- defp check_rate(conn, {limiter_name, [{scale, limit} | _], opts}) do
- bucket_name = bucket_name(conn, limiter_name, opts)
- ExRated.check_rate("#{bucket_name}:#{ip(conn)}", scale, limit)
- end
-
- defp check_rate(conn, {limiter_name, {scale, limit}, opts}) do
- check_rate(conn, {limiter_name, [{scale, limit}, {scale, limit}], opts})
- end
-
- def ip(%{remote_ip: remote_ip}) do
- remote_ip
- |> Tuple.to_list()
- |> Enum.join(".")
- end
-
- defp render_throttled_error(conn) do
- conn
- |> render_error(:too_many_requests, "Throttled")
- |> halt()
- end
-end
diff --git a/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex
new file mode 100644
index 000000000..187582ede
--- /dev/null
+++ b/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex
@@ -0,0 +1,44 @@
+defmodule Pleroma.Plugs.RateLimiter.LimiterSupervisor do
+ use DynamicSupervisor
+
+ import Cachex.Spec
+
+ def start_link(init_arg) do
+ DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
+ end
+
+ def add_limiter(limiter_name, expiration) do
+ {:ok, _pid} =
+ DynamicSupervisor.start_child(
+ __MODULE__,
+ %{
+ id: String.to_atom("rl_#{limiter_name}"),
+ start:
+ {Cachex, :start_link,
+ [
+ limiter_name,
+ [
+ expiration:
+ expiration(
+ default: expiration,
+ interval: check_interval(expiration),
+ lazy: true
+ )
+ ]
+ ]}
+ }
+ )
+ end
+
+ @impl true
+ def init(_init_arg) do
+ DynamicSupervisor.init(strategy: :one_for_one)
+ end
+
+ defp check_interval(exp) do
+ (exp / 2)
+ |> Kernel.trunc()
+ |> Kernel.min(5000)
+ |> Kernel.max(1)
+ end
+end
diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex
new file mode 100644
index 000000000..d720508c8
--- /dev/null
+++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex
@@ -0,0 +1,227 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.RateLimiter do
+ @moduledoc """
+
+ ## Configuration
+
+ A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
+
+ * The first element: `scale` (Integer). The time scale in milliseconds.
+ * The second element: `limit` (Integer). How many requests to limit in the time scale provided.
+
+ It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
+
+ To disable a limiter set its value to `nil`.
+
+ ### Example
+
+ config :pleroma, :rate_limit,
+ one: {1000, 10},
+ two: [{10_000, 10}, {10_000, 50}],
+ foobar: nil
+
+ Here we have three limiters:
+
+ * `one` which is not over 10req/1s
+ * `two` which has two limits: 10req/10s for unauthenticated users and 50req/10s for authenticated users
+ * `foobar` which is disabled
+
+ ## Usage
+
+ AllowedSyntax:
+
+ plug(Pleroma.Plugs.RateLimiter, name: :limiter_name)
+ plug(Pleroma.Plugs.RateLimiter, options) # :name is a required option
+
+ Allowed options:
+
+ * `name` required, always used to fetch the limit values from the config
+ * `bucket_name` overrides name for counting purposes (e.g. to have a separate limit for a set of actions)
+ * `params` appends values of specified request params (e.g. ["id"]) to bucket name
+
+ Inside a controller:
+
+ plug(Pleroma.Plugs.RateLimiter, [name: :one] when action == :one)
+ plug(Pleroma.Plugs.RateLimiter, [name: :two] when action in [:two, :three])
+
+ plug(
+ Pleroma.Plugs.RateLimiter,
+ [name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]]
+ when action in ~w(fav_status unfav_status)a
+ )
+
+ or inside a router pipeline:
+
+ pipeline :api do
+ ...
+ plug(Pleroma.Plugs.RateLimiter, name: :one)
+ ...
+ end
+ """
+ import Pleroma.Web.TranslationHelpers
+ import Plug.Conn
+
+ alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
+ alias Pleroma.User
+
+ def init(opts) do
+ limiter_name = Keyword.get(opts, :name)
+
+ case Pleroma.Config.get([:rate_limit, limiter_name]) do
+ nil ->
+ nil
+
+ config ->
+ name_root = Keyword.get(opts, :bucket_name, limiter_name)
+
+ %{
+ name: name_root,
+ limits: config,
+ opts: opts
+ }
+ end
+ end
+
+ # Do not limit if there is no limiter configuration
+ def call(conn, nil), do: conn
+
+ def call(conn, settings) do
+ settings
+ |> incorporate_conn_info(conn)
+ |> check_rate()
+ |> case do
+ {:ok, _count} ->
+ conn
+
+ {:error, _count} ->
+ render_throttled_error(conn)
+ end
+ end
+
+ def inspect_bucket(conn, name_root, settings) do
+ settings =
+ settings
+ |> incorporate_conn_info(conn)
+
+ bucket_name = make_bucket_name(%{settings | name: name_root})
+ key_name = make_key_name(settings)
+ limit = get_limits(settings)
+
+ case Cachex.get(bucket_name, key_name) do
+ {:error, :no_cache} ->
+ {:err, :not_found}
+
+ {:ok, nil} ->
+ {0, limit}
+
+ {:ok, value} ->
+ {value, limit - value}
+ end
+ end
+
+ defp check_rate(settings) do
+ bucket_name = make_bucket_name(settings)
+ key_name = make_key_name(settings)
+ limit = get_limits(settings)
+
+ case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
+ {:commit, value} ->
+ {:ok, value}
+
+ {:ignore, value} ->
+ {:error, value}
+
+ {:error, :no_cache} ->
+ initialize_buckets(settings)
+ check_rate(settings)
+ end
+ end
+
+ defp increment_value(nil, _limit), do: {:commit, 1}
+
+ defp increment_value(val, limit) when val >= limit, do: {:ignore, val}
+
+ defp increment_value(val, _limit), do: {:commit, val + 1}
+
+ defp incorporate_conn_info(settings, %{assigns: %{user: %User{id: user_id}}, params: params}) do
+ Map.merge(settings, %{
+ mode: :user,
+ conn_params: params,
+ conn_info: "#{user_id}"
+ })
+ end
+
+ defp incorporate_conn_info(settings, %{params: params} = conn) do
+ Map.merge(settings, %{
+ mode: :anon,
+ conn_params: params,
+ conn_info: "#{ip(conn)}"
+ })
+ end
+
+ defp ip(%{remote_ip: remote_ip}) do
+ remote_ip
+ |> Tuple.to_list()
+ |> Enum.join(".")
+ end
+
+ defp render_throttled_error(conn) do
+ conn
+ |> render_error(:too_many_requests, "Throttled")
+ |> halt()
+ end
+
+ defp make_key_name(settings) do
+ ""
+ |> attach_params(settings)
+ |> attach_identity(settings)
+ end
+
+ defp get_scale(_, {scale, _}), do: scale
+
+ defp get_scale(:anon, [{scale, _}, {_, _}]), do: scale
+
+ defp get_scale(:user, [{_, _}, {scale, _}]), do: scale
+
+ defp get_limits(%{limits: {_scale, limit}}), do: limit
+
+ defp get_limits(%{mode: :user, limits: [_, {_, limit}]}), do: limit
+
+ defp get_limits(%{limits: [{_, limit}, _]}), do: limit
+
+ defp make_bucket_name(%{mode: :user, name: name_root}),
+ do: user_bucket_name(name_root)
+
+ defp make_bucket_name(%{mode: :anon, name: name_root}),
+ do: anon_bucket_name(name_root)
+
+ defp attach_params(input, %{conn_params: conn_params, opts: opts}) do
+ param_string =
+ opts
+ |> Keyword.get(:params, [])
+ |> Enum.sort()
+ |> Enum.map(&Map.get(conn_params, &1, ""))
+ |> Enum.join(":")
+
+ "#{input}#{param_string}"
+ end
+
+ defp initialize_buckets(%{name: _name, limits: nil}), do: :ok
+
+ defp initialize_buckets(%{name: name, limits: limits}) do
+ LimiterSupervisor.add_limiter(anon_bucket_name(name), get_scale(:anon, limits))
+ LimiterSupervisor.add_limiter(user_bucket_name(name), get_scale(:user, limits))
+ end
+
+ defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
+ do: "user:#{base}:#{conn_info}"
+
+ defp attach_identity(base, %{mode: :anon, conn_info: conn_info}),
+ do: "ip:#{base}:#{conn_info}"
+
+ defp user_bucket_name(name_root), do: "user:#{name_root}" |> String.to_atom()
+ defp anon_bucket_name(name_root), do: "anon:#{name_root}" |> String.to_atom()
+end
diff --git a/lib/pleroma/plugs/rate_limiter/supervisor.ex b/lib/pleroma/plugs/rate_limiter/supervisor.ex
new file mode 100644
index 000000000..9672f7876
--- /dev/null
+++ b/lib/pleroma/plugs/rate_limiter/supervisor.ex
@@ -0,0 +1,16 @@
+defmodule Pleroma.Plugs.RateLimiter.Supervisor do
+ use Supervisor
+
+ def start_link(opts) do
+ Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
+ end
+
+ def init(_args) do
+ children = [
+ Pleroma.Plugs.RateLimiter.LimiterSupervisor
+ ]
+
+ opts = [strategy: :one_for_one, name: Pleroma.Web.Streamer.Supervisor]
+ Supervisor.init(children, opts)
+ end
+end
diff --git a/lib/pleroma/plugs/trailing_format_plug.ex b/lib/pleroma/plugs/trailing_format_plug.ex
index ce366b218..a4b8a406d 100644
--- a/lib/pleroma/plugs/trailing_format_plug.ex
+++ b/lib/pleroma/plugs/trailing_format_plug.ex
@@ -24,7 +24,8 @@ defmodule Pleroma.Plugs.TrailingFormatPlug do
"/api/help",
"/api/externalprofile",
"/notice",
- "/api/pleroma/emoji"
+ "/api/pleroma/emoji",
+ "/api/oauth_tokens"
]
def init(opts) do
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index bab8d92e2..09664db76 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -54,15 +54,7 @@ defmodule Pleroma.User.Search do
|> maybe_restrict_local(for_user)
end
- @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/
defp fts_search(query, query_string) do
- {nickname_weight, name_weight} =
- if String.match?(query_string, @nickname_regex) do
- {"A", "B"}
- else
- {"B", "A"}
- end
-
query_string = to_tsquery(query_string)
from(
@@ -70,12 +62,10 @@ defmodule Pleroma.User.Search do
where:
fragment(
"""
- (setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)) @@ to_tsquery('simple', ?)
+ (to_tsvector('simple', ?) || to_tsvector('simple', ?)) @@ to_tsquery('simple', ?)
""",
u.name,
- ^name_weight,
u.nickname,
- ^nickname_weight,
^query_string
)
)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 51a9c6169..65dd251f3 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -568,7 +568,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> fetch_activities_query(opts)
|> restrict_unlisted()
|> Pagination.fetch_paginated(opts, pagination)
- |> Enum.reverse()
end
@valid_visibilities ~w[direct unlisted public private]
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index b47618bde..30fc01755 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -607,10 +607,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
@doc "Force password reset for a given user"
- def force_password_reset(conn, %{"nickname" => nickname}) do
- (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
+ def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+ users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
- User.force_password_reset_async(user)
+ Enum.map(users, &User.force_password_reset_async/1)
+
+ ModerationLog.insert_log(%{
+ actor: admin,
+ subject: users,
+ action: "force_password_reset"
+ })
json_response(conn, :no_content, "")
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 2212e93f4..49735b5c2 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -12,7 +12,7 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Plugs.HTTPSecurityPlug)
plug(Pleroma.Plugs.UploadedMedia)
- @static_cache_control "public, no-cache"
+ @static_cache_control "public max-age=86400 must-revalidate"
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/feed_controller.ex
index d91ecef9c..d0e23007d 100644
--- a/lib/pleroma/web/feed/feed_controller.ex
+++ b/lib/pleroma/web/feed/feed_controller.ex
@@ -33,21 +33,22 @@ defmodule Pleroma.Web.Feed.FeedController do
def feed(conn, %{"nickname" => nickname} = params) do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
- query_params =
- params
- |> Map.take(["max_id"])
- |> Map.put("type", ["Create"])
- |> Map.put("whole_db", true)
- |> Map.put("actor_id", user.ap_id)
-
activities =
- query_params
+ %{
+ "type" => ["Create"],
+ "whole_db" => true,
+ "actor_id" => user.ap_id
+ }
+ |> Map.merge(Map.take(params, ["max_id"]))
|> ActivityPub.fetch_public_activities()
- |> Enum.reverse()
conn
|> put_resp_content_type("application/atom+xml")
- |> render("feed.xml", user: user, activities: activities)
+ |> render("feed.xml",
+ user: user,
+ activities: activities,
+ feed_config: Pleroma.Config.get([:feed])
+ )
end
end
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
index 5eef1e757..bb1332fd3 100644
--- a/lib/pleroma/web/feed/feed_view.ex
+++ b/lib/pleroma/web/feed/feed_view.ex
@@ -6,12 +6,23 @@ defmodule Pleroma.Web.Feed.FeedView do
use Phoenix.HTML
use Pleroma.Web, :view
+ alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.MediaProxy
require Pleroma.Constants
+ def prepare_activity(activity) do
+ object = activity_object(activity)
+
+ %{
+ activity: activity,
+ data: Map.get(object, :data),
+ object: object
+ }
+ end
+
def most_recent_update(activities, user) do
(List.first(activities) || user).updated_at
|> NaiveDateTime.to_iso8601()
@@ -23,31 +34,23 @@ defmodule Pleroma.Web.Feed.FeedView do
|> MediaProxy.url()
end
- def last_activity(activities) do
- List.last(activities)
- end
+ def last_activity(activities), do: List.last(activities)
- def activity_object(activity) do
- Object.normalize(activity)
- end
+ def activity_object(activity), do: Object.normalize(activity)
- def activity_object_data(activity) do
- activity
- |> activity_object()
- |> Map.get(:data)
+ def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
+ content
+ |> Formatter.truncate(opts[:max_length], opts[:omission])
+ |> escape()
end
- def activity_content(activity) do
- content = activity_object_data(activity)["content"]
-
+ def activity_content(%{data: %{"content" => content}}) do
content
|> String.replace(~r/[\n\r]/, "")
|> escape()
end
- def activity_context(activity) do
- activity.data["context"]
- end
+ def activity_context(activity), do: activity.data["context"]
def attachment_href(attachment) do
attachment["url"]
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 73fad519e..5b01b964b 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -66,9 +66,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@relations [:follow, :unfollow]
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
- plug(RateLimiter, {:relations_id_action, params: ["id", "uri"]} when action in @relations)
- plug(RateLimiter, :relations_actions when action in @relations)
- plug(RateLimiter, :app_account_creation when action == :create)
+ plug(RateLimiter, [name: :relations_id_action, params: ["id", "uri"]] when action in @relations)
+ plug(RateLimiter, [name: :relations_actions] when action in @relations)
+ plug(RateLimiter, [name: :app_account_creation] when action == :create)
plug(:assign_account_by_id when action in @needs_account)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
index bfd5120ba..d9e51de7f 100644
--- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
@local_mastodon_name "Mastodon-Local"
- plug(Pleroma.Plugs.RateLimiter, :password_reset when action == :password_reset)
+ plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
@doc "GET /web/login"
def login(%{assigns: %{user: %User{}}} = conn, _params) do
diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
index 6cfd68a84..0a929f55b 100644
--- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
- plug(RateLimiter, :search when action in [:search, :search2, :account_search])
+ plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search])
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, search_options(params, user))
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index e5d016f63..74b223cf4 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -82,17 +82,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
plug(
RateLimiter,
- {:status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]}
+ [name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]]
when action in ~w(reblog unreblog)a
)
plug(
RateLimiter,
- {:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
+ [name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]]
when action in ~w(favourite unfavourite)a
)
- plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
+ plug(RateLimiter, [name: :statuses_actions] when action in @rate_limited_status_actions)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
index f2d2d3ccb..384159336 100644
--- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
@@ -71,7 +71,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> ActivityPub.fetch_public_activities()
- |> Enum.reverse()
conn
|> add_link_headers(activities, %{"local" => local_only})
@@ -110,7 +109,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> Map.put("tag_all", tag_all)
|> Map.put("tag_reject", tag_reject)
|> ActivityPub.fetch_public_activities()
- |> Enum.reverse()
conn
|> add_link_headers(activities, %{"local" => local_only})
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index e9d2735b3..c5998e661 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -34,7 +34,11 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
id: participation.id |> to_string(),
accounts: render(AccountView, "index.json", users: users, as: :user),
unread: !participation.read,
- last_status: render(StatusView, "show.json", activity: activity, for: user)
+ last_status:
+ render(StatusView, "show.json",
+ activity: activity,
+ direct_conversation_id: participation.id
+ )
}
end
end
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index b785ca9d4..baff54151 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -243,7 +243,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
direct_conversation_id =
- with {_, true} <- {:include_id, opts[:with_direct_conversation_id]},
+ with {_, nil} <- {:direct_conversation_id, opts[:direct_conversation_id]},
+ {_, true} <- {:include_id, opts[:with_direct_conversation_id]},
{_, %User{} = for_user} <- {:for_user, opts[:for]},
%{data: %{"context" => context}} when is_binary(context) <- activity,
%Conversation{} = conversation <- Conversation.get_for_ap_id(context),
@@ -251,6 +252,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
Participation.for_user_and_conversation(for_user, conversation) do
participation_id
else
+ {:direct_conversation_id, participation_id} when is_integer(participation_id) ->
+ participation_id
+
_e ->
nil
end
diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
index 6ed181cff..358600e7d 100644
--- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
+++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
@@ -10,8 +10,8 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
alias Pleroma.Repo
alias Pleroma.User
- plug(RateLimiter, :authentication when action in [:user_exists, :check_password])
- plug(RateLimiter, {:authentication, params: ["user"]} when action == :check_password)
+ plug(RateLimiter, [name: :authentication] when action in [:user_exists, :check_password])
+ plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password)
def user_exists(conn, %{"user" => username}) do
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 192984242..d7ae503f6 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -58,6 +58,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
"polls",
"pleroma_explicit_addressing",
"shareable_emoji_packs",
+ "multifetch",
if Config.get([:media_proxy, :enabled]) do
"media_proxy"
end,
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 6fba9f968..2aee8cab2 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
use Pleroma.Web, :controller
alias Pleroma.Helpers.UriHelper
+ alias Pleroma.Plugs.RateLimiter
alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.User
@@ -24,7 +25,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
plug(:fetch_session)
plug(:fetch_flash)
- plug(Pleroma.Plugs.RateLimiter, :authentication when action == :create_authorization)
+ plug(RateLimiter, [name: :authentication] when action == :create_authorization)
action_fallback(Pleroma.Web.OAuth.FallbackController)
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 6958519de..12a7c2365 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Fallback.RedirectController
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Plugs.RateLimiter
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.ActivityPub.ObjectView
@@ -17,8 +18,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.Router
plug(
- Pleroma.Plugs.RateLimiter,
- {:ap_routes, params: ["uuid"]} when action in [:object, :activity]
+ RateLimiter,
+ [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
)
plug(
diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
index db6faac83..bc2f1017c 100644
--- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
@@ -42,7 +42,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
when action != :confirmation_resend
)
- plug(RateLimiter, :account_confirmation_resend when action == :confirmation_resend)
+ plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex
index d376e2069..16b1a53d2 100644
--- a/lib/pleroma/web/rel_me.ex
+++ b/lib/pleroma/web/rel_me.ex
@@ -25,13 +25,13 @@ defmodule Pleroma.Web.RelMe do
def parse(_), do: {:error, "No URL provided"}
defp parse_url(url) do
- {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
-
- data =
- Floki.attribute(html, "link[rel~=me]", "href") ++
- Floki.attribute(html, "a[rel~=me]", "href")
-
- {:ok, data}
+ with {:ok, %Tesla.Env{body: html, status: status}} when status in 200..299 <-
+ Pleroma.HTTP.get(url, [], adapter: @hackney_options),
+ data <-
+ Floki.attribute(html, "link[rel~=me]", "href") ++
+ Floki.attribute(html, "a[rel~=me]", "href") do
+ {:ok, data}
+ end
rescue
e -> {:error, "Parsing error: #{inspect(e)}"}
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index f69c5c2bc..8fb4aec13 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -171,7 +171,7 @@ defmodule Pleroma.Web.Router do
post("/users/email_invite", AdminAPIController, :email_invite)
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
- patch("/users/:nickname/force_password_reset", AdminAPIController, :force_password_reset)
+ patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
get("/users", AdminAPIController, :list_users)
get("/users/:nickname", AdminAPIController, :user_show)
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex
index d1f5e903c..514eacaed 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex
@@ -2,11 +2,13 @@
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<id><%= @data["id"] %></id>
- <title><%= "New note by #{@user.nickname}" %></title>
- <content type="html"><%= activity_content(@activity) %></content>
+ <title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
+ <content type="html"><%= activity_content(@object) %></content>
<published><%= @data["published"] %></published>
<updated><%= @data["published"] %></updated>
- <ostatus:conversation ref="<%= activity_context(@activity) %>"><%= activity_context(@activity) %></ostatus:conversation>
+ <ostatus:conversation ref="<%= activity_context(@activity) %>">
+ <%= activity_context(@activity) %>
+ </ostatus:conversation>
<link ref="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
<%= if @data["summary"] do %>
diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex
index 45df9dc09..5ae36d345 100644
--- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex
+++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex
@@ -19,6 +19,6 @@
<% end %>
<%= for activity <- @activities do %>
- <%= render @view_module, "_activity.xml", Map.merge(assigns, %{activity: activity, data: activity_object_data(activity)}) %>
+ <%= render @view_module, "_activity.xml", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</feed>