Update PingAdmins to show triggering event (#3853)

This commit is contained in:
Mac Siri 2019-09-03 10:23:25 -04:00 committed by GitHub
parent 0d73cfe0a3
commit 5e75bf3aaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 50 deletions

View file

@ -51,7 +51,7 @@ class CommentsController < ApplicationController
# POST /comments.json
def create
authorize Comment
raise if RateLimitChecker.new(current_user).limit_by_situation("comment_creation")
raise if RateLimitChecker.new(current_user).limit_by_action("comment_creation")
@comment = Comment.new(permitted_attributes(Comment))
@comment.user_id = current_user.id

View file

@ -5,7 +5,7 @@ class ImageUploadsController < ApplicationController
def create
authorize :image_upload
begin
raise RateLimitChecker::UploadRateLimitReached if RateLimitChecker.new(current_user).limit_by_situation("image_upload")
raise RateLimitChecker::UploadRateLimitReached if RateLimitChecker.new(current_user).limit_by_action("image_upload")
raise CarrierWave::IntegrityError if params[:image].blank?
uploaders = image_upload(params[:image])

View file

@ -1,8 +1,8 @@
class RateLimitCheckerJob < ApplicationJob
queue_as :rate_limit_checker
def perform(user_id)
def perform(user_id, action)
user = User.find_by(id: user_id)
PingAdmins.call(user) if user
PingAdmins.call(user, action) if user
end
end

View file

@ -1,13 +1,14 @@
class RateLimitChecker
attr_accessor :user
attr_reader :user, :action
def initialize(user = nil)
@user = user
end
class UploadRateLimitReached < StandardError; end
def limit_by_situation(situation)
result = case situation
def limit_by_action(action)
result = case action
when "comment_creation"
user.comments.where("created_at > ?", 30.seconds.ago).size > 9
when "published_article_creation"
@ -17,7 +18,10 @@ class RateLimitChecker
else
false
end
ping_admins if result == true
if result
@action = action
ping_admins
end
result
end
@ -28,16 +32,16 @@ class RateLimitChecker
end
def limit_by_email_recipient_address(address)
# This is related to the recipient, not the "user" initiator, like in situation.
# This is related to the recipient, not the "user" initiator, like in action.
EmailMessage.where(to: address).
where("sent_at > ?", 2.minutes.ago).size > 5
end
def ping_admins
RateLimitCheckerJob.perform_later(user.id)
RateLimitCheckerJob.perform_later(user.id, action)
end
def ping_admins_without_delay
RateLimitCheckerJob.perform_now(user.id)
RateLimitCheckerJob.perform_now(user.id, action)
end
end

View file

@ -10,7 +10,7 @@ module Articles
end
def call
raise if RateLimitChecker.new(user).limit_by_situation("published_article_creation")
raise if RateLimitChecker.new(user).limit_by_action("published_article_creation")
tags = article_params[:tags]
series = article_params[:series]

View file

@ -1,6 +1,7 @@
class PingAdmins
def initialize(user)
def initialize(user, action = "unknown")
@user = user
@action = action
end
def self.call(*args)
@ -11,7 +12,7 @@ class PingAdmins
return unless user && Rails.env.production?
SlackBot.ping(
"Rate limit exceeded. https://dev.to#{user.path}",
"Rate limit exceeded (#{action}). https://dev.to#{user.path}",
channel: "abuse-reports",
username: "rate_limit",
icon_emoji: ":hand:",
@ -20,5 +21,5 @@ class PingAdmins
private
attr_reader :user
attr_reader :user, :action
end

View file

@ -17,15 +17,15 @@ RSpec.describe RateLimitCheckerJob, type: :job do
it "calls a service" do
perform_enqueued_jobs do
described_class.perform_now(user.id)
described_class.perform_now(user.id, "test")
expect(service).to have_received(:call).with(user).once
expect(service).to have_received(:call).with(user, "test").once
end
end
it "does nothing for non-existent user" do
perform_enqueued_jobs do
described_class.perform_now(nil)
described_class.perform_now(nil, "test")
expect(service).not_to have_received(:call)
end

View file

@ -4,41 +4,48 @@ RSpec.describe RateLimitChecker do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
it ".limit_by_situation returns false for invalid case" do
expect(described_class.new(user).limit_by_situation("random-nothing")).to eq(false)
end
it ".limit_by_situation returns true if too many comments at once" do
create_list(:comment, 10, user_id: user.id, commentable_id: article.id)
expect(described_class.new(user).limit_by_situation("comment_creation")).to eq(true)
end
it ".limit_by_situation returns false if allowed comment" do
create_list(:comment, 2, user_id: user.id, commentable_id: article.id)
expect(described_class.new(user).limit_by_situation("comment_creation")).to eq(false)
end
it ". limit_by_situation returns true if too many published articles at once" do
create_list(:article, 10, user_id: user.id, published: true)
expect(described_class.new(user).limit_by_situation("published_article_creation")).to eq(true)
end
it ".limit_by_situation returns false if published articles comment" do
create_list(:article, 2, user_id: user.id, published: true)
expect(described_class.new(user).limit_by_situation("published_article_creation")).to eq(false)
end
it ".limit_by_email_recipient_address returns true if too many published articles at once" do
10.times do
EmailMessage.create(to: user.email, sent_at: Time.current)
describe "#limit_by_action" do
it "returns false for invalid action" do
expect(described_class.new(user).limit_by_action("random-nothing")).to eq(false)
end
it "returns true if too many comments at once" do
create_list(:comment, 10, user_id: user.id, commentable_id: article.id)
expect(described_class.new(user).limit_by_action("comment_creation")).to eq(true)
end
it "triggers ping admin when too many comments" do
allow(RateLimitCheckerJob).to receive(:perform_later)
create_list(:comment, 10, user_id: user.id, commentable_id: article.id)
described_class.new(user).limit_by_action("comment_creation")
expect(RateLimitCheckerJob).to have_received(:perform_later).with(user.id, "comment_creation")
end
it "returns false if allowed comment" do
create_list(:comment, 2, user_id: user.id, commentable_id: article.id)
expect(described_class.new(user).limit_by_action("comment_creation")).to eq(false)
end
it "returns true if too many published articles at once" do
create_list(:article, 10, user_id: user.id, published: true)
expect(described_class.new(user).limit_by_action("published_article_creation")).to eq(true)
end
it "returns false if published articles comment" do
create_list(:article, 2, user_id: user.id, published: true)
expect(described_class.new(user).limit_by_action("published_article_creation")).to eq(false)
end
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(true)
end
it ".limit_by_email_recipient_address returns false if published articles comment" do
2.times do
EmailMessage.create(to: user.email, sent_at: Time.current)
describe "#limit_by_email_recipient_address" do
it "returns true if too many published articles at once" do
10.times { EmailMessage.create(to: user.email, sent_at: Time.current) }
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(true)
end
it "returns false if published articles comment" do
2.times { EmailMessage.create(to: user.email, sent_at: Time.current) }
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(false)
end
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(false)
end
end