forked from treehouse/mastodon
Limit the number of people that can be followed from one account (#8807)
Configurable soft limit of 7,500, and above that, configurable ratio of 1.1 * followers, controlled by: - MAX_FOLLOWS_THRESHOLD - MAX_FOLLOWS_RATIO Fix #2311rebase/4.0.0rc2
parent
186024a058
commit
a46ab86adf
|
@ -25,6 +25,7 @@ class Follow < ApplicationRecord
|
||||||
has_one :notification, as: :activity, dependent: :destroy
|
has_one :notification, as: :activity, dependent: :destroy
|
||||||
|
|
||||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||||
|
validates_with FollowLimitValidator, on: :create
|
||||||
|
|
||||||
scope :recent, -> { reorder(id: :desc) }
|
scope :recent, -> { reorder(id: :desc) }
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class FollowRequest < ApplicationRecord
|
||||||
has_one :notification, as: :activity, dependent: :destroy
|
has_one :notification, as: :activity, dependent: :destroy
|
||||||
|
|
||||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||||
|
validates_with FollowLimitValidator, on: :create
|
||||||
|
|
||||||
def authorize!
|
def authorize!
|
||||||
account.follow!(target_account, reblogs: show_reblogs, uri: uri)
|
account.follow!(target_account, reblogs: show_reblogs, uri: uri)
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class FollowLimitValidator < ActiveModel::Validator
|
||||||
|
LIMIT = ENV.fetch('MAX_FOLLOWS_THRESHOLD', 7_500).to_i
|
||||||
|
RATIO = ENV.fetch('MAX_FOLLOWS_RATIO', 1.1).to_f
|
||||||
|
|
||||||
|
def validate(follow)
|
||||||
|
return if follow.account.nil? || !follow.account.local?
|
||||||
|
follow.errors.add(:base, I18n.t('users.follow_limit_reached', limit: self.class.limit_for_account(follow.account))) if limit_reached?(follow.account)
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def limit_for_account(account)
|
||||||
|
if account.following_count < LIMIT
|
||||||
|
LIMIT
|
||||||
|
else
|
||||||
|
account.followers_count * RATIO
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def limit_reached?(account)
|
||||||
|
account.following_count >= self.class.limit_for_account(account)
|
||||||
|
end
|
||||||
|
end
|
|
@ -37,6 +37,8 @@ class ImportWorker
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_rows
|
def import_rows
|
||||||
CSV.new(import_contents).reject(&:blank?)
|
rows = CSV.new(import_contents).reject(&:blank?)
|
||||||
|
rows = rows.take(FollowLimitValidator.limit_for_account(@import.account)) if @import.type == 'following'
|
||||||
|
rows
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -917,6 +917,7 @@ en:
|
||||||
tips: Tips
|
tips: Tips
|
||||||
title: Welcome aboard, %{name}!
|
title: Welcome aboard, %{name}!
|
||||||
users:
|
users:
|
||||||
|
follow_limit_reached: You cannot follow more than %{limit} people
|
||||||
invalid_email: The e-mail address is invalid
|
invalid_email: The e-mail address is invalid
|
||||||
invalid_otp_token: Invalid two-factor code
|
invalid_otp_token: Invalid two-factor code
|
||||||
otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
|
otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
|
||||||
|
|
|
@ -23,6 +23,20 @@ RSpec.describe Follow, type: :model do
|
||||||
follow.valid?
|
follow.valid?
|
||||||
expect(follow).to model_have_error_on_field(:target_account)
|
expect(follow).to model_have_error_on_field(:target_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'is invalid if account already follows too many people' do
|
||||||
|
alice.update(following_count: FollowLimitValidator::LIMIT)
|
||||||
|
|
||||||
|
expect(subject).to_not be_valid
|
||||||
|
expect(subject).to model_have_error_on_field(:base)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is valid if account is only on the brink of following too many people' do
|
||||||
|
alice.update(following_count: FollowLimitValidator::LIMIT - 1)
|
||||||
|
|
||||||
|
expect(subject).to be_valid
|
||||||
|
expect(subject).to_not model_have_error_on_field(:base)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'recent' do
|
describe 'recent' do
|
||||||
|
|
Loading…
Reference in New Issue