Wrap error messages into gettext helpers
[akkoma] / lib / pleroma / plugs / rate_limiter.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Plugs.RateLimiter do
6 @moduledoc """
7
8 ## Configuration
9
10 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:
11
12 * The first element: `scale` (Integer). The time scale in milliseconds.
13 * The second element: `limit` (Integer). How many requests to limit in the time scale provided.
14
15 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.
16
17 To disable a limiter set its value to `nil`.
18
19 ### Example
20
21 config :pleroma, :rate_limit,
22 one: {1000, 10},
23 two: [{10_000, 10}, {10_000, 50}],
24 foobar: nil
25
26 Here we have three limiters:
27
28 * `one` which is not over 10req/1s
29 * `two` which has two limits: 10req/10s for unauthenticated users and 50req/10s for authenticated users
30 * `foobar` which is disabled
31
32 ## Usage
33
34 Inside a controller:
35
36 plug(Pleroma.Plugs.RateLimiter, :one when action == :one)
37 plug(Pleroma.Plugs.RateLimiter, :two when action in [:two, :three])
38
39 or inside a router pipiline:
40
41 pipeline :api do
42 ...
43 plug(Pleroma.Plugs.RateLimiter, :one)
44 ...
45 end
46 """
47 import Pleroma.Web.TranslationHelpers
48 import Plug.Conn
49
50 alias Pleroma.User
51
52 def init(limiter_name) do
53 case Pleroma.Config.get([:rate_limit, limiter_name]) do
54 nil -> nil
55 config -> {limiter_name, config}
56 end
57 end
58
59 # do not limit if there is no limiter configuration
60 def call(conn, nil), do: conn
61
62 def call(conn, opts) do
63 case check_rate(conn, opts) do
64 {:ok, _count} -> conn
65 {:error, _count} -> render_throttled_error(conn)
66 end
67 end
68
69 defp check_rate(%{assigns: %{user: %User{id: user_id}}}, {limiter_name, [_, {scale, limit}]}) do
70 ExRated.check_rate("#{limiter_name}:#{user_id}", scale, limit)
71 end
72
73 defp check_rate(conn, {limiter_name, [{scale, limit} | _]}) do
74 ExRated.check_rate("#{limiter_name}:#{ip(conn)}", scale, limit)
75 end
76
77 defp check_rate(conn, {limiter_name, {scale, limit}}) do
78 check_rate(conn, {limiter_name, [{scale, limit}]})
79 end
80
81 def ip(%{remote_ip: remote_ip}) do
82 remote_ip
83 |> Tuple.to_list()
84 |> Enum.join(".")
85 end
86
87 defp render_throttled_error(conn) do
88 conn
89 |> render_error(:too_many_requests, "Throttled")
90 |> halt()
91 end
92 end