Open garethrees opened 1 year ago
Extract from https://github.com/mysociety/alaveteli/pull/7602 to be able to apply to any User association records.
User
Here's a sketch.
diff --git a/app/models/user.rb b/app/models/user.rb index ceb0eab68..f1653a9bc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -36,7 +36,42 @@ # closed_at :datetime # login_token :string # +class ContentRateLimiter + def self.from_relation(records) + new(records.klass, records) + end + + def initialize(klass, records) + @klass = klass + @records = records + end + + def exceeded_limit? + exceeded_daily_cap? || exceeded_creation_rate? + end + + def exceeded_daily_cap? + return false unless defined?(klass::MAX_PER_DAY) + # OR (created_at: 24.hours.ago..) + records.where(created_at: Time.zone.now.at_beginning_of_day..).size >= + klass::MAX_PER_DAY + end + + def exceeded_creation_rate? + return false unless defined?(klass::CREATION_RATE_INTERVALS) + + klass::CREATION_RATE_INTERVALS.any? do |limit, duration| + return false if records.size < limit + records.limit(limit).all? { |record| record.created_at > duration.ago } + end + end + + private + + def records + records.reorder(created_at: :desc) + end +end class User < ApplicationRecord include AlaveteliFeatures::Helpers include AlaveteliPro::PhaseCounts @@ -89,10 +124,17 @@ class User < ApplicationRecord -> { order(created_at: :desc) }, inverse_of: :user, dependent: :destroy + has_many :comments, -> { order(created_at: :desc) }, inverse_of: :user, - dependent: :destroy + dependent: :destroy do + def limiter + ContentRateLimiter.new(proxy_association.reflection, proxy_association.target) + end + end + + has_many :public_body_change_requests, -> { order(created_at: :desc) }, inverse_of: :user, @@ -451,9 +493,7 @@ def can_make_followup? def can_make_comments? return false unless active? return true if is_admin? || is_pro_admin? - - !exceeded_limit?(:comments) && - !Comment.exceeded_creation_rate?(comments) + !comments.limiter.exceeded_limit? end def can_contact_other_users?
Rails is getting built-in rate limiting! https://edgeapi.rubyonrails.org/classes/ActionController/RateLimiting/ClassMethods.html#method-i-rate_limit
Extract from https://github.com/mysociety/alaveteli/pull/7602 to be able to apply to any
User
association records.Here's a sketch.