From 749722a9dfbfe204e10624886c3444e512dbc821 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Sat, 16 Mar 2024 23:19:47 +0200 Subject: [PATCH] add rate limit --- app/controllers/api/api_base_controller.rb | 6 ++++++ app/controllers/application_controller.rb | 6 ++++++ .../send_submission_email_controller.rb | 2 ++ lib/rate_limit.rb | 19 +++++++++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 lib/rate_limit.rb diff --git a/app/controllers/api/api_base_controller.rb b/app/controllers/api/api_base_controller.rb index b4aa8ee1..86ead819 100644 --- a/app/controllers/api/api_base_controller.rb +++ b/app/controllers/api/api_base_controller.rb @@ -19,6 +19,12 @@ module Api render json: { error: e.message }, status: :unprocessable_entity end + rescue_from RateLimit::LimitApproached do |e| + Rollbar.error(e) if defined?(Rollbar) + + render json: { error: 'Too many requests' }, status: :too_many_requests + end + if Rails.env.production? rescue_from CanCan::AccessDenied do |e| Rollbar.warning(e) if defined?(Rollbar) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 19547214..0b3dd112 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,6 +22,12 @@ class ApplicationController < ActionController::Base redirect_to request.path end + rescue_from RateLimit::LimitApproached do |e| + Rollbar.error(e) if defined?(Rollbar) + + redirect_to request.referer, alert: 'Too many requests', status: :too_many_requests + end + if Rails.env.production? rescue_from CanCan::AccessDenied do |e| Rollbar.warning(e) if defined?(Rollbar) diff --git a/app/controllers/send_submission_email_controller.rb b/app/controllers/send_submission_email_controller.rb index 4a3fa1e0..8e74c348 100644 --- a/app/controllers/send_submission_email_controller.rb +++ b/app/controllers/send_submission_email_controller.rb @@ -21,6 +21,8 @@ class SendSubmissionEmailController < ApplicationController Submitter.find_by!(slug: params[:submitter_slug]) end + RateLimit.call("send-email-#{@submitter.id}", limit: 2, ttl: 5.minutes) + SubmitterMailer.documents_copy_email(@submitter, sig: true).deliver_later! respond_to do |f| diff --git a/lib/rate_limit.rb b/lib/rate_limit.rb new file mode 100644 index 00000000..3f01705c --- /dev/null +++ b/lib/rate_limit.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module RateLimit + LimitApproached = Class.new(StandardError) + + STORE = ActiveSupport::Cache::MemoryStore.new + + module_function + + def call(key, limit:, ttl:, enabled: Docuseal.multitenant?) + return true unless enabled + + value = STORE.increment(key, 1, expires_in: ttl) + + raise LimitApproached if value > limit + + true + end +end