From ad2ed5fe51575cd531d6608fddb11f87dd314f73 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 10 Oct 2013 10:32:03 +1100 Subject: [PATCH] rate limits for topics and posts on first day max_topics_in_first_day and max_replies_in_first_day --- app/models/post.rb | 7 ++++++ app/models/site_setting.rb | 3 +++ app/models/topic.rb | 3 +++ config/locales/server.en.yml | 4 ++++ lib/site_setting_extension.rb | 6 ++++- spec/models/topic_spec.rb | 42 ++++++++++++++++++++++++++++------- spec/spec_helper.rb | 23 +++---------------- 7 files changed, 59 insertions(+), 29 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 8e837c94dc7..30afdb38fdb 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -17,6 +17,7 @@ class Post < ActiveRecord::Base versioned if: :raw_changed? rate_limit + rate_limit :limit_posts_per_day belongs_to :user belongs_to :topic, counter_cache: :posts_count @@ -54,6 +55,12 @@ class Post < ActiveRecord::Base @types ||= Enum.new(:regular, :moderator_action) end + def limit_posts_per_day + if user.created_at > 1.day.ago && post_number > 1 + RateLimiter.new(user, "first-day-replies-per-day:#{Date.today.to_s}", SiteSetting.max_replies_in_first_day, 1.day.to_i) + end + end + def trash!(trashed_by=nil) self.topic_links.each(&:destroy) super(trashed_by) diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 1d771e6fb16..655ff0c4ba6 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -66,6 +66,9 @@ class SiteSetting < ActiveRecord::Base setting(:flags_required_to_hide_post, 3) setting(:cooldown_minutes_after_hiding_posts, 10) + setting(:max_topics_in_first_day, 5) + setting(:max_replies_in_first_day, 10) + setting(:num_flags_to_block_new_user, 3) setting(:num_users_to_block_new_user, 3) setting(:notify_mods_when_user_blocked, false) diff --git a/app/models/topic.rb b/app/models/topic.rb index cf0452906ce..e397aba3975 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -183,6 +183,9 @@ class Topic < ActiveRecord::Base # Additional rate limits on topics: per day and private messages per day def limit_topics_per_day RateLimiter.new(user, "topics-per-day:#{Date.today.to_s}", SiteSetting.max_topics_per_day, 1.day.to_i) + if user.created_at > 1.day.ago + RateLimiter.new(user, "first-day-topics-per-day:#{Date.today.to_s}", SiteSetting.max_topics_in_first_day, 1.day.to_i) + end end def limit_private_messages_per_day diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 329f6388701..b5447e2db90 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -530,6 +530,10 @@ en: flags_required_to_hide_post: "Number of flags that cause a post to be automatically hidden and PM sent to the user (0 for never)" cooldown_minutes_after_hiding_posts: "Number of minutes a user must wait before they can edit a post hidden via flagging" + + max_topics_in_first_day: "The maximum number of topics a user is alowed to create in thier first day on the site" + max_replies_in_first_day: "The maximum number of replies a user is alowed to create in thier first day on the site" + num_flags_to_block_new_user: "If a new user's posts get this many spam flags from (n) different users, hide all their posts and prevent future posting. 0 disables this feature." num_users_to_block_new_user: "If a new user's posts get (x) spam flags from this many different users, hide all their posts and prevent future posting. 0 disables this feature." notify_mods_when_user_blocked: "If a user is automatically blocked, send a message to all moderators." diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index f9f181f7a32..1b45dd5d86e 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -67,10 +67,14 @@ module SiteSettingExtension def client_settings_json Rails.cache.fetch(SiteSettingExtension.client_settings_cache_key, expires_in: 30.minutes) do - MultiJson.dump(Hash[*@@client_settings.map{|n| [n, self.send(n)]}.flatten]) + client_settings_json_uncached end end + def client_settings_json_uncached + MultiJson.dump(Hash[*@@client_settings.map{|n| [n, self.send(n)]}.flatten]) + end + # Retrieve all settings def all_settings @defaults.map do |s, v| diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 1d9e6beb26e..c2f6864a148 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -565,10 +565,9 @@ describe Topic do shared_examples_for "adding a star to a topic" do it 'triggers a forum topic user change with true' do # otherwise no chance the mock will work - freeze_time do - TopicUser.expects(:change).with(@user, @topic.id, starred: true, starred_at: DateTime.now, unstarred_at: nil) - @topic.toggle_star(@user, true) - end + freeze_time + TopicUser.expects(:change).with(@user, @topic.id, starred: true, starred_at: DateTime.now, unstarred_at: nil) + @topic.toggle_star(@user, true) end it 'increases the star_count of the forum topic' do @@ -603,10 +602,9 @@ describe Topic do end it 'triggers a forum topic user change with false' do - freeze_time do - TopicUser.expects(:change).with(@user, @topic.id, starred: false, unstarred_at: DateTime.now) - @topic.toggle_star(@user, false) - end + freeze_time + TopicUser.expects(:change).with(@user, @topic.id, starred: false, unstarred_at: DateTime.now) + @topic.toggle_star(@user, false) end it 'reduces the star_count' do @@ -1181,4 +1179,32 @@ describe Topic do end end end + + it "limits new users to max_topics_in_first_day and max_posts_in_first_day" do + SiteSetting.stubs(:max_topics_in_first_day).returns(1) + SiteSetting.stubs(:max_replies_in_first_day).returns(1) + RateLimiter.stubs(:disabled?).returns(false) + SiteSetting.stubs(:client_settings_json).returns(SiteSetting.client_settings_json_uncached) + + start = Time.now.tomorrow.beginning_of_day + + freeze_time(start) + + user = Fabricate(:user) + topic_id = create_post(user: user).topic_id + + freeze_time(start + 10.minutes) + lambda { + create_post(user: user) + }.should raise_exception + + freeze_time(start + 20.minutes) + create_post(user: user, topic_id: topic_id) + + freeze_time(start + 30.minutes) + + lambda { + create_post(user: user, topic_id: topic_id) + }.should raise_exception + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 939f399f5ae..b658128f384 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -85,26 +85,9 @@ Spork.prefork do end - class DateTime - class << self - alias_method :old_now, :now - def now - @now || old_now - end - def now=(v) - @now = v - end - end - end - - def freeze_time(d=nil) - begin - d ||= DateTime.now - DateTime.now = d - yield - ensure - DateTime.now = nil - end + def freeze_time(now=Time.now) + DateTime.stubs(:now).returns(DateTime.parse(now.to_s)) + Time.stubs(:now).returns(Time.parse(now.to_s)) end end