diff --git a/lib/philomena_web/controllers/conversation_controller.ex b/lib/philomena_web/controllers/conversation_controller.ex index 07fcfe00..bfb9cf7d 100644 --- a/lib/philomena_web/controllers/conversation_controller.ex +++ b/lib/philomena_web/controllers/conversation_controller.ex @@ -9,6 +9,10 @@ defmodule PhilomenaWeb.ConversationController do plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create] + plug PhilomenaWeb.LimitPlug, + [time: 60, error: "You may only create a conversation once every minute."] + when action in [:create] + plug :load_and_authorize_resource, model: Conversation, id_field: "slug", diff --git a/lib/philomena_web/controllers/image/comment_controller.ex b/lib/philomena_web/controllers/image/comment_controller.ex index a5e3de95..216123fd 100644 --- a/lib/philomena_web/controllers/image/comment_controller.ex +++ b/lib/philomena_web/controllers/image/comment_controller.ex @@ -8,6 +8,10 @@ defmodule PhilomenaWeb.Image.CommentController do alias Philomena.Comments alias Philomena.Images + plug PhilomenaWeb.LimitPlug, + [time: 30, error: "You may only create a comment once every 30 seconds."] + when action in [:create] + plug PhilomenaWeb.FilterBannedUsersPlug when action in [:create, :edit, :update] plug PhilomenaWeb.UserAttributionPlug when action in [:create] diff --git a/lib/philomena_web/controllers/image/source_controller.ex b/lib/philomena_web/controllers/image/source_controller.ex index 0d4c58cd..e8356b34 100644 --- a/lib/philomena_web/controllers/image/source_controller.ex +++ b/lib/philomena_web/controllers/image/source_controller.ex @@ -8,6 +8,10 @@ defmodule PhilomenaWeb.Image.SourceController do alias Philomena.Repo import Ecto.Query + plug PhilomenaWeb.LimitPlug, + [time: 5, error: "You may only update metadata once every 5 seconds."] + when action in [:update] + plug PhilomenaWeb.FilterBannedUsersPlug plug PhilomenaWeb.CaptchaPlug plug PhilomenaWeb.UserAttributionPlug diff --git a/lib/philomena_web/controllers/image/tag_controller.ex b/lib/philomena_web/controllers/image/tag_controller.ex index b987f983..33590706 100644 --- a/lib/philomena_web/controllers/image/tag_controller.ex +++ b/lib/philomena_web/controllers/image/tag_controller.ex @@ -10,6 +10,10 @@ defmodule PhilomenaWeb.Image.TagController do alias Philomena.Repo import Ecto.Query + plug PhilomenaWeb.LimitPlug, + [time: 5, error: "You may only update metadata once every 5 seconds."] + when action in [:update] + plug PhilomenaWeb.FilterBannedUsersPlug plug PhilomenaWeb.CaptchaPlug plug PhilomenaWeb.UserAttributionPlug diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 010dcf70..0f3f6c68 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -18,6 +18,10 @@ defmodule PhilomenaWeb.ImageController do alias Philomena.Repo import Ecto.Query + plug PhilomenaWeb.LimitPlug, + [time: 10, error: "You may only upload images once every 10 seconds."] + when action in [:create] + plug :load_image when action in [:show] plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create] diff --git a/lib/philomena_web/controllers/topic/post_controller.ex b/lib/philomena_web/controllers/topic/post_controller.ex index 535c6cff..296d580a 100644 --- a/lib/philomena_web/controllers/topic/post_controller.ex +++ b/lib/philomena_web/controllers/topic/post_controller.ex @@ -5,6 +5,10 @@ defmodule PhilomenaWeb.Topic.PostController do alias Philomena.Posts alias Philomena.UserStatistics + plug PhilomenaWeb.LimitPlug, + [time: 30, error: "You may only make a post once every 30 seconds."] + when action in [:create] + plug PhilomenaWeb.FilterBannedUsersPlug plug PhilomenaWeb.UserAttributionPlug diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex index 79d16d64..04f1be4f 100644 --- a/lib/philomena_web/controllers/topic_controller.ex +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -9,6 +9,10 @@ defmodule PhilomenaWeb.TopicController do alias Philomena.Repo import Ecto.Query + plug PhilomenaWeb.LimitPlug, + [time: 300, error: "You may only make a new topic once every 5 minutes."] + when action in [:create] + plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create] plug PhilomenaWeb.UserAttributionPlug when action in [:new, :create] plug PhilomenaWeb.AdvertPlug when action in [:show] diff --git a/lib/philomena_web/plugs/limit_plug.ex b/lib/philomena_web/plugs/limit_plug.ex new file mode 100644 index 00000000..bc1ea8d2 --- /dev/null +++ b/lib/philomena_web/plugs/limit_plug.ex @@ -0,0 +1,54 @@ +defmodule PhilomenaWeb.LimitPlug do + @moduledoc """ + This plug automatically limits requests which are submitted faster + than should be allowed for a given client. + + ## Example + + plug PhilomenaWeb.LimitPlug, [time: 30, error: "Too fast! Slow down."] + """ + + alias Plug.Conn + alias Phoenix.Controller + + @doc false + @spec init(any()) :: any() + def init(opts), do: opts + + @doc false + @spec call(Conn.t(), any()) :: Conn.t() + def call(conn, opts) do + limit = Keyword.get(opts, :limit, 1) + time = Keyword.get(opts, :time, 5) + error = Keyword.get(opts, :error) + + data = [ + current_user_id(conn.assigns.current_user), + :inet_parse.ntoa(conn.remote_ip), + conn.private.phoenix_action, + conn.private.phoenix_controller + ] + + key = "rl-#{Enum.join(data, "")}" + + [amt, _] = + Redix.pipeline!(:redix, [ + ["INCR", key], + ["EXPIRE", key, time] + ]) + + cond do + amt <= limit -> + conn + + true -> + conn + |> Controller.put_flash(:error, error) + |> Controller.redirect(external: conn.assigns.referrer) + |> Conn.halt() + end + end + + defp current_user_id(%{id: id}), do: id + defp current_user_id(_), do: nil +end