mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2026-02-15 17:16:57 +00:00
ConfigDB, RateLimiter, RateLimit: Use new type to parse and cast rate limits.
This commit is contained in:
parent
47f4bde0ea
commit
bd61916270
3 changed files with 128 additions and 31 deletions
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
|
|||
import Pleroma.Web.Gettext
|
||||
|
||||
alias __MODULE__
|
||||
alias Pleroma.EctoType.Config.RateLimit
|
||||
alias Pleroma.Repo
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
|
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|
|||
|> cast(params, [:key, :group, :value])
|
||||
|> validate_required([:key, :group, :value])
|
||||
|> unique_constraint(:key, name: :config_group_key_index)
|
||||
|> validate_rate_limit()
|
||||
end
|
||||
|
||||
defp validate_rate_limit(changeset) do
|
||||
group = get_field(changeset, :group)
|
||||
key = get_field(changeset, :key)
|
||||
|
||||
if group == :pleroma and key == :rate_limit do
|
||||
value = get_field(changeset, :value)
|
||||
|
||||
case normalize_rate_limit(value) do
|
||||
{:ok, normalized_value} ->
|
||||
put_change(changeset, :value, normalized_value)
|
||||
|
||||
{:error, {limiter_name, reason}} ->
|
||||
add_error(
|
||||
changeset,
|
||||
:value,
|
||||
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
|
||||
)
|
||||
end
|
||||
else
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_rate_limit(nil), do: {:ok, nil}
|
||||
|
||||
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
|
||||
|
||||
defp normalize_rate_limit(value) when is_list(value) do
|
||||
if Keyword.keyword?(value) do
|
||||
value
|
||||
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
|
||||
case RateLimit.cast_with_error(limiter_value) do
|
||||
{:ok, normalized_limiter_value} ->
|
||||
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
|
||||
|
||||
{:error, reason} ->
|
||||
{:halt, {:error, {limiter_name, reason}}}
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
{:ok, acc} -> {:ok, Enum.reverse(acc)}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
else
|
||||
{:error, {:rate_limit, "must be a keyword list"}}
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
|
||||
|
||||
defp create(params) do
|
||||
%ConfigDB{}
|
||||
|> changeset(params)
|
||||
|
|
|
|||
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.EctoType.Config.RateLimit do
|
||||
@moduledoc false
|
||||
|
||||
use Ecto.Type
|
||||
|
||||
@type t ::
|
||||
nil
|
||||
| {non_neg_integer(), non_neg_integer()}
|
||||
| [{non_neg_integer(), non_neg_integer()}]
|
||||
|
||||
@impl true
|
||||
def type, do: :term
|
||||
|
||||
@impl true
|
||||
def cast(value) do
|
||||
case cast_with_error(value) do
|
||||
{:ok, normalized} -> {:ok, normalized}
|
||||
{:error, _reason} -> :error
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def load(value), do: cast(value)
|
||||
|
||||
@impl true
|
||||
def dump(value), do: cast(value)
|
||||
|
||||
@spec cast_with_error(term()) :: {:ok, t()} | {:error, String.t()}
|
||||
def cast_with_error(nil), do: {:ok, nil}
|
||||
|
||||
def cast_with_error({scale, limit}) do
|
||||
with {:ok, scale} <- parse_integer(scale, "scale"),
|
||||
{:ok, limit} <- parse_integer(limit, "limit"),
|
||||
true <- scale >= 1 and limit >= 1 do
|
||||
{:ok, {scale, limit}}
|
||||
else
|
||||
false -> {:error, "scale and limit must be >= 1"}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def cast_with_error([{_, _} = unauth, {_, _} = auth]) do
|
||||
with {:ok, unauth} <- cast_with_error(unauth),
|
||||
{:ok, auth} <- cast_with_error(auth) do
|
||||
{:ok, [unauth, auth]}
|
||||
else
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def cast_with_error(_),
|
||||
do:
|
||||
{:error, "must be a {scale, limit} tuple, a [{scale, limit}, {scale, limit}] list, or nil"}
|
||||
|
||||
defp parse_integer(value, _label) when is_integer(value), do: {:ok, value}
|
||||
|
||||
defp parse_integer(value, label) when is_binary(value) do
|
||||
value = String.trim(value)
|
||||
|
||||
case Integer.parse(value) do
|
||||
{number, ""} -> {:ok, number}
|
||||
_ -> {:error, "#{label} must be an integer"}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_integer(_value, label), do: {:error, "#{label} must be an integer"}
|
||||
end
|
||||
|
|
@ -68,6 +68,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Config.Holder
|
||||
alias Pleroma.EctoType.Config.RateLimit
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor
|
||||
|
||||
|
|
@ -214,40 +215,13 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
|
||||
defp normalize_limits(nil), do: :disabled
|
||||
|
||||
defp normalize_limits({scale, limit}) do
|
||||
with {:ok, scale} <- normalize_integer(scale),
|
||||
{:ok, limit} <- normalize_integer(limit),
|
||||
true <- scale >= 1 and limit >= 1 do
|
||||
{:ok, {scale, limit}}
|
||||
else
|
||||
_ -> :error
|
||||
defp normalize_limits(limits) do
|
||||
case RateLimit.cast(limits) do
|
||||
{:ok, normalized_limits} -> {:ok, normalized_limits}
|
||||
:error -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_limits([{_, _} = first, {_, _} = second]) do
|
||||
with {:ok, first} <- normalize_limits(first),
|
||||
{:ok, second} <- normalize_limits(second) do
|
||||
{:ok, [first, second]}
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_limits(_), do: :error
|
||||
|
||||
defp normalize_integer(value) when is_integer(value), do: {:ok, value}
|
||||
|
||||
defp normalize_integer(value) when is_binary(value) do
|
||||
value = String.trim(value)
|
||||
|
||||
case Integer.parse(value) do
|
||||
{number, ""} -> {:ok, number}
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_integer(_), do: :error
|
||||
|
||||
defp check_rate(action_settings) do
|
||||
bucket_name = make_bucket_name(action_settings)
|
||||
key_name = make_key_name(action_settings)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue