aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorlain <lain@soykaf.club>2019-06-11 11:32:01 +0000
committerlain <lain@soykaf.club>2019-06-11 11:32:01 +0000
commit63ab3c30ebb33241074cb252c449cd7879b77870 (patch)
tree31221764c19bfc33cf74639898a9148ae8fe06cd /lib
parent3e3dcd223d9525fe63c84d0dc62a793c6661bbbe (diff)
parent7c063a898d60cadd324087999226314586b72bd3 (diff)
downloadpleroma-63ab3c30ebb33241074cb252c449cd7879b77870.tar.gz
Merge branch 'feature/rate-limiter' into 'develop'
Feature/Rate Limiter Closes #943 See merge request pleroma/pleroma!1266
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/plugs/rate_limit_plug.ex36
-rw-r--r--lib/pleroma/plugs/rate_limiter.ex87
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex10
3 files changed, 89 insertions, 44 deletions
diff --git a/lib/pleroma/plugs/rate_limit_plug.ex b/lib/pleroma/plugs/rate_limit_plug.ex
deleted file mode 100644
index 466f64a79..000000000
--- a/lib/pleroma/plugs/rate_limit_plug.ex
+++ /dev/null
@@ -1,36 +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.RateLimitPlug do
- import Phoenix.Controller, only: [json: 2]
- import Plug.Conn
-
- def init(opts), do: opts
-
- def call(conn, opts) do
- enabled? = Pleroma.Config.get([:app_account_creation, :enabled])
-
- case check_rate(conn, Map.put(opts, :enabled, enabled?)) do
- {:ok, _count} -> conn
- {:error, _count} -> render_error(conn)
- %Plug.Conn{} = conn -> conn
- end
- end
-
- defp check_rate(conn, %{enabled: true} = opts) do
- max_requests = opts[:max_requests]
- bucket_name = conn.remote_ip |> Tuple.to_list() |> Enum.join(".")
-
- ExRated.check_rate(bucket_name, opts[:interval] * 1000, max_requests)
- end
-
- defp check_rate(conn, _), do: conn
-
- defp render_error(conn) do
- conn
- |> put_status(:forbidden)
- |> json(%{error: "Rate limit exceeded."})
- |> halt()
- end
-end
diff --git a/lib/pleroma/plugs/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter.ex
new file mode 100644
index 000000000..e02ba4213
--- /dev/null
+++ b/lib/pleroma/plugs/rate_limiter.ex
@@ -0,0 +1,87 @@
+# 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.
+
+ ### Example
+
+ config :pleroma, :rate_limit,
+ one: {1000, 10},
+ two: [{10_000, 10}, {10_000, 50}]
+
+ Here we have two limiters: `one` which is not over 10req/1s and `two` which has two limits 10req/10s for unauthenticated users and 50req/10s for authenticated users.
+
+ ## Usage
+
+ Inside a controller:
+
+ plug(Pleroma.Plugs.RateLimiter, :one when action == :one)
+ plug(Pleroma.Plugs.RateLimiter, :two when action in [:two, :three])
+
+ or inside a router pipiline:
+
+ pipeline :api do
+ ...
+ plug(Pleroma.Plugs.RateLimiter, :one)
+ ...
+ end
+ """
+
+ import Phoenix.Controller, only: [json: 2]
+ import Plug.Conn
+
+ alias Pleroma.User
+
+ def init(limiter_name) do
+ case Pleroma.Config.get([:rate_limit, limiter_name]) do
+ nil -> nil
+ config -> {limiter_name, config}
+ end
+ end
+
+ # do not limit if there is no limiter configuration
+ def call(conn, nil), do: conn
+
+ def call(conn, opts) do
+ case check_rate(conn, opts) do
+ {:ok, _count} -> conn
+ {:error, _count} -> render_error(conn)
+ end
+ end
+
+ defp check_rate(%{assigns: %{user: %User{id: user_id}}}, {limiter_name, [_, {scale, limit}]}) do
+ ExRated.check_rate("#{limiter_name}:#{user_id}", scale, limit)
+ end
+
+ defp check_rate(conn, {limiter_name, [{scale, limit} | _]}) do
+ ExRated.check_rate("#{limiter_name}:#{ip(conn)}", scale, limit)
+ end
+
+ defp check_rate(conn, {limiter_name, {scale, limit}}) do
+ check_rate(conn, {limiter_name, [{scale, limit}]})
+ end
+
+ def ip(%{remote_ip: remote_ip}) do
+ remote_ip
+ |> Tuple.to_list()
+ |> Enum.join(".")
+ end
+
+ defp render_error(conn) do
+ conn
+ |> put_status(:too_many_requests)
+ |> json(%{error: "Throttled"})
+ |> halt()
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 92cd77f62..46049dd24 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -46,14 +46,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
require Logger
- plug(
- Pleroma.Plugs.RateLimitPlug,
- %{
- max_requests: Config.get([:app_account_creation, :max_requests]),
- interval: Config.get([:app_account_creation, :interval])
- }
- when action in [:account_register]
- )
+ plug(Pleroma.Plugs.RateLimiter, :app_account_creation when action == :account_register)
+ plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
@local_mastodon_name "Mastodon-Local"