diff --git a/app/controllers/hledger/reports_controller.rb b/app/controllers/hledger/reports_controller.rb index 046f65a..05579d0 100644 --- a/app/controllers/hledger/reports_controller.rb +++ b/app/controllers/hledger/reports_controller.rb @@ -11,6 +11,7 @@ module ::Hledger before_action :ensure_enabled before_action :find_topic before_action :enforce_rate_limit + before_action :enforce_global_rate_limit def show report_type = params[:report_type].to_s @@ -87,6 +88,13 @@ module ::Hledger ).performed! end + def enforce_global_rate_limit + max = SiteSetting.hledger_global_rate_limit_per_minute.to_i + return if max <= 0 + + RateLimiter.new(nil, "hledger-reports-global", max, 60, global: true).performed! + end + def parse_date(value) return nil if value.blank? diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 515b8dc..5a54f75 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -6,6 +6,7 @@ en: hledger_timeout_seconds: "Maximum number of seconds an hledger command may run" hledger_max_output_bytes: "Maximum number of bytes captured from hledger output" hledger_memory_limit_mb: "Maximum memory in megabytes the hledger process may use; set to 0 to disable the limit" + hledger_global_rate_limit_per_minute: "Maximum number of hledger reports that may be generated per minute across all users; set to 0 to disable the limit" hledger: errors: unsupported_report: "Unsupported report type." diff --git a/config/settings.yml b/config/settings.yml index f2bd600..6a1419e 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -13,3 +13,6 @@ hledger: hledger_memory_limit_mb: default: 256 min: 0 + hledger_global_rate_limit_per_minute: + default: 120 + min: 0 diff --git a/spec/requests/hledger/reports_spec.rb b/spec/requests/hledger/reports_spec.rb index 6924eb2..89ea54b 100644 --- a/spec/requests/hledger/reports_spec.rb +++ b/spec/requests/hledger/reports_spec.rb @@ -175,4 +175,20 @@ RSpec.describe Hledger::ReportsController do expect(response.status).to eq(404) end + + it "enforces the global rate limit" do + RateLimiter.enable + RateLimiter.clear_all_global! + SiteSetting.hledger_global_rate_limit_per_minute = 1 + + get_report("accounts") + expect(response.status).to eq(200) + + get_report("accounts") + expect(response.status).to eq(429) + ensure + RateLimiter.disable + RateLimiter.clear_all_global! + SiteSetting.hledger_global_rate_limit_per_minute = 120 + end end