aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2020-12-26 23:52:46 +0100
committerGitHub <noreply@github.com>2020-12-26 23:52:46 +0100
commitf1f96ebf02e96d21d84c52825cbac623b66488f8 (patch)
treeb0185cd71c95891a5aa04f859db0e213d66f32f3
parent4580129c987371b656a8ab19feba09fb98f9fac1 (diff)
downloadmastodon-f1f96ebf02e96d21d84c52825cbac623b66488f8.tar
mastodon-f1f96ebf02e96d21d84c52825cbac623b66488f8.tar.gz
mastodon-f1f96ebf02e96d21d84c52825cbac623b66488f8.tar.bz2
mastodon-f1f96ebf02e96d21d84c52825cbac623b66488f8.zip
Fix being able to import more than allowed number of follows (#15384)
* Fix being able to import more than allowed number of follows Without this commit, if someone tries importing a second list of accounts to follow before the first one has been processed, this will queue imports for the two whole lists, even if they exceed the account's allowed number of outgoing follows. This commit changes it so the individual queued imports aren't exempt from the follow limit check (they remain exempt from the rate-limiting check though). * Catch validation errors to not re-queue failed follows Co-authored-by: Claire <claire.github-309c@sitedethib.com>
-rw-r--r--app/models/concerns/account_interactions.rb8
-rw-r--r--app/models/concerns/follow_limitable.rb17
-rw-r--r--app/models/follow.rb2
-rw-r--r--app/models/follow_request.rb2
-rw-r--r--app/services/follow_service.rb7
-rw-r--r--app/workers/authorize_follow_worker.rb2
-rw-r--r--app/workers/import/relationship_worker.rb6
-rw-r--r--app/workers/refollow_worker.rb2
-rw-r--r--app/workers/unfollow_follow_worker.rb2
-rw-r--r--lib/mastodon/accounts_cli.rb2
-rw-r--r--spec/workers/refollow_worker_spec.rb4
11 files changed, 38 insertions, 16 deletions
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb
index e2c4b8acf..974f57820 100644
--- a/app/models/concerns/account_interactions.rb
+++ b/app/models/concerns/account_interactions.rb
@@ -97,8 +97,8 @@ module AccountInteractions
has_many :announcement_mutes, dependent: :destroy
end
- def follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false)
- rel = active_relationships.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, uri: uri, rate_limit: rate_limit)
+ def follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false, bypass_limit: false)
+ rel = active_relationships.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, uri: uri, rate_limit: rate_limit, bypass_follow_limit: bypass_limit)
.find_or_create_by!(target_account: other_account)
rel.show_reblogs = reblogs unless reblogs.nil?
@@ -111,8 +111,8 @@ module AccountInteractions
rel
end
- def request_follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false)
- rel = follow_requests.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, uri: uri, rate_limit: rate_limit)
+ def request_follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false, bypass_limit: false)
+ rel = follow_requests.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, uri: uri, rate_limit: rate_limit, bypass_follow_limit: bypass_limit)
.find_or_create_by!(target_account: other_account)
rel.show_reblogs = reblogs unless reblogs.nil?
diff --git a/app/models/concerns/follow_limitable.rb b/app/models/concerns/follow_limitable.rb
new file mode 100644
index 000000000..c64060d6e
--- /dev/null
+++ b/app/models/concerns/follow_limitable.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module FollowLimitable
+ extend ActiveSupport::Concern
+
+ included do
+ validates_with FollowLimitValidator, on: :create, unless: :bypass_follow_limit?
+ end
+
+ def bypass_follow_limit=(value)
+ @bypass_follow_limit = value
+ end
+
+ def bypass_follow_limit?
+ @bypass_follow_limit
+ end
+end
diff --git a/app/models/follow.rb b/app/models/follow.rb
index 69a1722b3..a5e3fe809 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -17,6 +17,7 @@ class Follow < ApplicationRecord
include Paginable
include RelationshipCacheable
include RateLimitable
+ include FollowLimitable
rate_limit by: :account, family: :follows
@@ -26,7 +27,6 @@ class Follow < ApplicationRecord
has_one :notification, as: :activity, dependent: :destroy
validates :account_id, uniqueness: { scope: :target_account_id }
- validates_with FollowLimitValidator, on: :create, if: :rate_limit?
scope :recent, -> { reorder(id: :desc) }
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index 2d2a77b59..59fefcdf6 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -17,6 +17,7 @@ class FollowRequest < ApplicationRecord
include Paginable
include RelationshipCacheable
include RateLimitable
+ include FollowLimitable
rate_limit by: :account, family: :follows
@@ -26,7 +27,6 @@ class FollowRequest < ApplicationRecord
has_one :notification, as: :activity, dependent: :destroy
validates :account_id, uniqueness: { scope: :target_account_id }
- validates_with FollowLimitValidator, on: :create, if: :rate_limit?
def authorize!
account.follow!(target_account, reblogs: show_reblogs, notify: notify, uri: uri)
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index 962572851..b98f7011d 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -11,11 +11,12 @@ class FollowService < BaseService
# @option [Boolean] :reblogs Whether or not to show reblogs, defaults to true
# @option [Boolean] :notify Whether to create notifications about new posts, defaults to false
# @option [Boolean] :bypass_locked
+ # @option [Boolean] :bypass_limit Allow following past the total follow number
# @option [Boolean] :with_rate_limit
def call(source_account, target_account, options = {})
@source_account = source_account
@target_account = ResolveAccountService.new.call(target_account, skip_webfinger: true)
- @options = { bypass_locked: false, with_rate_limit: false }.merge(options)
+ @options = { bypass_locked: false, bypass_limit: false, with_rate_limit: false }.merge(options)
raise ActiveRecord::RecordNotFound if following_not_possible?
raise Mastodon::NotPermittedError if following_not_allowed?
@@ -54,7 +55,7 @@ class FollowService < BaseService
end
def request_follow!
- follow_request = @source_account.request_follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit])
+ follow_request = @source_account.request_follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
if @target_account.local?
LocalNotificationWorker.perform_async(@target_account.id, follow_request.id, follow_request.class.name, :follow_request)
@@ -66,7 +67,7 @@ class FollowService < BaseService
end
def direct_follow!
- follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit])
+ follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow)
MergeWorker.perform_async(@target_account.id, @source_account.id)
diff --git a/app/workers/authorize_follow_worker.rb b/app/workers/authorize_follow_worker.rb
index 0d5014624..f57900fa5 100644
--- a/app/workers/authorize_follow_worker.rb
+++ b/app/workers/authorize_follow_worker.rb
@@ -7,7 +7,7 @@ class AuthorizeFollowWorker
source_account = Account.find(source_account_id)
target_account = Account.find(target_account_id)
- AuthorizeFollowService.new.call(source_account, target_account)
+ AuthorizeFollowService.new.call(source_account, target_account, bypass_limit: true)
rescue ActiveRecord::RecordNotFound
true
end
diff --git a/app/workers/import/relationship_worker.rb b/app/workers/import/relationship_worker.rb
index 4a455f3ae..4a7100435 100644
--- a/app/workers/import/relationship_worker.rb
+++ b/app/workers/import/relationship_worker.rb
@@ -15,7 +15,11 @@ class Import::RelationshipWorker
case relationship
when 'follow'
- FollowService.new.call(from_account, target_account, options)
+ begin
+ FollowService.new.call(from_account, target_account, options)
+ rescue ActiveRecord::RecordInvalid
+ raise if FollowLimitValidator.limit_for_account(from_account) < from_account.following_count
+ end
when 'unfollow'
UnfollowService.new.call(from_account, target_account)
when 'block'
diff --git a/app/workers/refollow_worker.rb b/app/workers/refollow_worker.rb
index 98940680d..319b00109 100644
--- a/app/workers/refollow_worker.rb
+++ b/app/workers/refollow_worker.rb
@@ -19,7 +19,7 @@ class RefollowWorker
# Schedule re-follow
begin
- FollowService.new.call(follower, target_account, reblogs: reblogs, notify: notify)
+ FollowService.new.call(follower, target_account, reblogs: reblogs, notify: notify, bypass_limit: true)
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError
next
end
diff --git a/app/workers/unfollow_follow_worker.rb b/app/workers/unfollow_follow_worker.rb
index 71b5a0e3f..0bd5ff472 100644
--- a/app/workers/unfollow_follow_worker.rb
+++ b/app/workers/unfollow_follow_worker.rb
@@ -14,7 +14,7 @@ class UnfollowFollowWorker
reblogs = follow&.show_reblogs?
notify = follow&.notify?
- FollowService.new.call(follower_account, new_target_account, reblogs: reblogs, notify: notify, bypass_locked: bypass_locked)
+ FollowService.new.call(follower_account, new_target_account, reblogs: reblogs, notify: notify, bypass_locked: bypass_locked, bypass_limit: true)
UnfollowService.new.call(follower_account, old_target_account, skip_unmerge: true)
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
true
diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb
index cdd1db995..653bfca30 100644
--- a/lib/mastodon/accounts_cli.rb
+++ b/lib/mastodon/accounts_cli.rb
@@ -384,7 +384,7 @@ module Mastodon
end
processed, = parallelize_with_progress(Account.local.without_suspended) do |account|
- FollowService.new.call(account, target_account)
+ FollowService.new.call(account, target_account, bypass_limit: true)
end
say("OK, followed target from #{processed} accounts", :green)
diff --git a/spec/workers/refollow_worker_spec.rb b/spec/workers/refollow_worker_spec.rb
index 6b4c04291..df6731b64 100644
--- a/spec/workers/refollow_worker_spec.rb
+++ b/spec/workers/refollow_worker_spec.rb
@@ -23,8 +23,8 @@ describe RefollowWorker do
result = subject.perform(account.id)
expect(result).to be_nil
- expect(service).to have_received(:call).with(alice, account, reblogs: true, notify: false)
- expect(service).to have_received(:call).with(bob, account, reblogs: false, notify: false)
+ expect(service).to have_received(:call).with(alice, account, reblogs: true, notify: false, bypass_limit: true)
+ expect(service).to have_received(:call).with(bob, account, reblogs: false, notify: false, bypass_limit: true)
end
end
end