[WiP] Whenever a remote keypair changes, unfollow them and re-subscribe to … (#4907)
* Whenever a remote keypair changes, unfollow them and re-subscribe to them In Mastodon (it could be different for other OStatus or AP-enabled software), a keypair change is indicative of whole user (or instance) data loss. In this situation, the “new” user might be different, and almost certainly has an empty followers list. In this case, Mastodon instances will disagree on follower lists, leading to unreliable delivery and “shadow followers”, that is users believed by a remote instance to be followers, without the affected user knowing. Drawbacks of this change are: 1. If an user legitimately changes public key for some reason without losing data (not possible in Mastodon at the moment), they will have their remote followers unsubscribed/re-subscribed needlessly. 2. Depending of the number of remote followers, this may generate quite some traffic. 3. If the user change is an attempt at usurpation, the remote followers will unknowingly follow the usurper. Note that this is *not* a change of behavior, Mastodon already behaves like that, although delivery might be unreliable, and the usurper would not have known the former user's followers. * Rename ResubscribeWorker to RefollowWorker * Process followers in batchesgh/stable
parent
af10c9fbff
commit
f29918e707
|
@ -17,7 +17,9 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
|
||||
create_account if @account.nil?
|
||||
upgrade_account if @account.ostatus?
|
||||
old_public_key = @account.public_key
|
||||
update_account
|
||||
RefollowWorker.perform_async(@account.id) if old_public_key != @account.public_key
|
||||
|
||||
@account
|
||||
rescue Oj::ParseError
|
||||
|
|
|
@ -85,8 +85,10 @@ class ResolveRemoteAccountService < BaseService
|
|||
|
||||
def handle_ostatus
|
||||
create_account if @account.nil?
|
||||
old_public_key = @account.public_key
|
||||
update_account
|
||||
update_account_profile if update_profile?
|
||||
RefollowWorker.perform_async(@account.id) if old_public_key != @account.public_key
|
||||
end
|
||||
|
||||
def update_profile?
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RefollowWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', retry: false
|
||||
|
||||
def perform(target_account_id)
|
||||
target_account = Account.find(target_account_id)
|
||||
|
||||
target_account.followers.where(domain: nil).find_each do |follower|
|
||||
# Locally unfollow remote account
|
||||
follower.unfollow!(target_account)
|
||||
|
||||
# Schedule re-follow
|
||||
begin
|
||||
FollowService.new.call(follower, target_account)
|
||||
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue