]> cat aescling's git repositories - mastodon.git/commitdiff
[WiP] Whenever a remote keypair changes, unfollow them and re-subscribe to … (#4907)
authorThibG <thib@sitedethib.com>
Tue, 12 Sep 2017 21:10:40 +0000 (23:10 +0200)
committerEugen Rochko <eugen@zeonfederated.com>
Tue, 12 Sep 2017 21:10:40 +0000 (23:10 +0200)
* 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 batches

app/services/activitypub/process_account_service.rb
app/services/resolve_remote_account_service.rb
app/workers/refollow_worker.rb [new file with mode: 0644]

index b54e447ad06d7055ae1461ca3dbd01cb56e21ffe..badb2672062bdc72d99c32f719811e76dbbfde75 100644 (file)
@@ -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
index 7031c98f5ee4875fbbe6da5a0020ff32d0cdd308..75360150151e1e5af92c2de4c343c1ef5c19ef00 100644 (file)
@@ -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?
diff --git a/app/workers/refollow_worker.rb b/app/workers/refollow_worker.rb
new file mode 100644 (file)
index 0000000..9c42d42
--- /dev/null
@@ -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