From 84673fe79dd6c67e28492633269eedc3beecbd54 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 28 Feb 2024 08:51:26 +0900 Subject: [PATCH] =?UTF-8?q?Add:=20#591=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E4=BF=9D=E7=95=99=E4=B8=AD=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=8B=E3=82=89=E3=83=A1=E3=83=B3=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=8C=E6=9D=A5=E3=81=9F=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=ABuri=E3=82=92=E8=A8=98=E9=8C=B2=E3=81=97=E3=80=81?= =?UTF-8?q?=E6=89=BF=E8=AA=8D=E6=99=82=E3=81=AB=E3=83=95=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=83=81=E3=81=97=E3=81=AB=E8=A1=8C=E3=81=8F=E5=87=A6=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/activitypub/activity/create.rb | 17 ++++++++++ app/lib/activitypub/activity/follow.rb | 12 +++---- app/models/account.rb | 2 +- app/models/pending_status.rb | 18 ++++++++++ ...rb => activate_follow_requests_service.rb} | 2 +- .../activate_remote_statuses_service.rb | 29 ++++++++++++++++ .../activitypub/process_collection_service.rb | 2 +- ...r.rb => activate_remote_account_worker.rb} | 5 +-- .../activitypub/fetch_remove_status_worker.rb | 17 ++++++++++ .../20240227225017_create_pending_statuses.rb | 13 +++++++ db/schema.rb | 15 +++++++- lib/tasks/dangerous.rake | 2 ++ spec/lib/activitypub/activity/create_spec.rb | 34 +++++++++++++++++++ ... activate_follow_requests_service_spec.rb} | 2 +- 14 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 app/models/pending_status.rb rename app/services/{enable_follow_requests_service.rb => activate_follow_requests_service.rb} (95%) create mode 100644 app/services/activate_remote_statuses_service.rb rename app/workers/{enable_follow_requests_worker.rb => activate_remote_account_worker.rb} (60%) create mode 100644 app/workers/activitypub/fetch_remove_status_worker.rb create mode 100644 db/migrate/20240227225017_create_pending_statuses.rb rename spec/services/{enable_follow_requests_service_spec.rb => activate_follow_requests_service_spec.rb} (96%) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d59436c0814508..c7b25980542dd6 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -50,6 +50,11 @@ def message_franking def create_status return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity? + if @account.suspended? + process_pending_status if @account.remote_pending? + return + end + with_redis_lock("create:#{object_uri}") do return if delete_arrived_first?(object_uri) || poll_vote? @@ -405,6 +410,18 @@ def poll_vote! ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, replied_to_status.id) unless replied_to_status.preloadable_poll.hide_totals? end + def process_pending_status + return if PendingStatus.exists?(uri: @object['id']) + + fetch_account = as_array(@object['tag']) + .filter_map { |tag| equals_or_includes?(tag['type'], 'Mention') && tag['href'] && ActivityPub::TagManager.instance.local_uri?(tag['href']) && ActivityPub::TagManager.instance.uri_to_resource(tag['href'], Account) } + .first + fetch_account ||= (audience_to + audience_cc).filter_map { |uri| ActivityPub::TagManager.instance.local_uri?(uri) && ActivityPub::TagManager.instance.uri_to_resource(uri, Account) }.first + fetch_account ||= Account.representative + + PendingStatus.create!(account: @account, uri: @object['id'], fetch_account: fetch_account) + end + def resolve_thread(status) return unless status.reply? && status.thread.nil? && Request.valid_url?(in_reply_to_uri) diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 677b9e069ef3d9..75c88d964edc75 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -20,6 +20,11 @@ def perform return end + if @account.suspended? + PendingFollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) if @account.remote_pending? + return + end + if target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved? || target_account.instance_actor? || block_new_follow? reject_follow_request!(target_account) return @@ -33,13 +38,6 @@ def perform return end - if @account.suspended? && @account.remote_pending? - PendingFollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) - return - elsif @account.suspended? - return - end - follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) if request_pending_follow?(@account, target_account) diff --git a/app/models/account.rb b/app/models/account.rb index 02d58e78089b0e..1146791612ddca 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -300,7 +300,7 @@ def unsuspend! def approve_remote! update!(remote_pending: false) unsuspend! - EnableFollowRequestsWorker.perform_async(id) + ActivateRemoteAccountWorker.perform_async(id) end def reject_remote! diff --git a/app/models/pending_status.rb b/app/models/pending_status.rb new file mode 100644 index 00000000000000..3b596028cef38c --- /dev/null +++ b/app/models/pending_status.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: pending_statuses +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# fetch_account_id :bigint(8) not null +# uri :string not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class PendingStatus < ApplicationRecord + belongs_to :account + belongs_to :fetch_account, class_name: 'Account' +end diff --git a/app/services/enable_follow_requests_service.rb b/app/services/activate_follow_requests_service.rb similarity index 95% rename from app/services/enable_follow_requests_service.rb rename to app/services/activate_follow_requests_service.rb index 74e280619ea4d9..3c4641f7cd2258 100644 --- a/app/services/enable_follow_requests_service.rb +++ b/app/services/activate_follow_requests_service.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class EnableFollowRequestsService < BaseService +class ActivateFollowRequestsService < BaseService include Payloadable include FollowHelper diff --git a/app/services/activate_remote_statuses_service.rb b/app/services/activate_remote_statuses_service.rb new file mode 100644 index 00000000000000..cc2180e74bfb96 --- /dev/null +++ b/app/services/activate_remote_statuses_service.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ActivateRemoteStatusesService < BaseService + include Payloadable + include FollowHelper + + def call(account) + @account = account + + PendingStatus.transaction do + PendingStatus.where(account: account).find_each do |status_info| + approve_status!(status_info) + end + end + end + + private + + def approve_status!(pending) + account_id = pending.account_id + fetch_account_id = pending.fetch_account_id + uri = pending.uri + pending.destroy! + + return if ActivityPub::TagManager.instance.uri_to_resource(uri, Status).present? + + FetchRemoteStatusWorker.perform_async(uri, account_id, fetch_account_id) + end +end diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index aaa9a9d668fd11..202a3640ef4142 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -56,7 +56,7 @@ def activity_allowed_while_suspended? end def activity_allowed_while_remote_pending? - %w(Follow).include?(@json['type']) || activity_allowed_while_suspended? + %w(Follow Create).include?(@json['type']) || activity_allowed_while_suspended? end def process_items(items) diff --git a/app/workers/enable_follow_requests_worker.rb b/app/workers/activate_remote_account_worker.rb similarity index 60% rename from app/workers/enable_follow_requests_worker.rb rename to app/workers/activate_remote_account_worker.rb index b0993b2e224827..c122cdeb548425 100644 --- a/app/workers/enable_follow_requests_worker.rb +++ b/app/workers/activate_remote_account_worker.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class EnableFollowRequestsWorker +class ActivateRemoteAccountWorker include Sidekiq::Worker def perform(account_id) @@ -8,6 +8,7 @@ def perform(account_id) return true if account.nil? return true if account.suspended? - EnableFollowRequestsService.new.call(account) + ActivateFollowRequestsService.new.call(account) + ActivateRemoteStatusesService.new.call(account) end end diff --git a/app/workers/activitypub/fetch_remove_status_worker.rb b/app/workers/activitypub/fetch_remove_status_worker.rb new file mode 100644 index 00000000000000..945cec4708a017 --- /dev/null +++ b/app/workers/activitypub/fetch_remove_status_worker.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class ActivityPub::FetchRemoteStatusWorker + include Sidekiq::Worker + include Redisable + + sidekiq_options queue: 'pull', retry: 3 + + def perform(uri, author_account_id, on_behalf_of_account_id) + author = Account.find(author_account_id) + on_behalf_of = on_behalf_of_account_id.present? ? Account.find(on_behalf_of_account_id) : nil + + ActivityPub::FetchRemoteStatusService.new.call(uri, on_behalf_of: on_behalf_of, expected_actor_uri: author, request_id: uri) + rescue ActiveRecord::RecordNotFound, Mastodon::RaceConditionError + true + end +end diff --git a/db/migrate/20240227225017_create_pending_statuses.rb b/db/migrate/20240227225017_create_pending_statuses.rb new file mode 100644 index 00000000000000..dd471631f8794d --- /dev/null +++ b/db/migrate/20240227225017_create_pending_statuses.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreatePendingStatuses < ActiveRecord::Migration[7.1] + def change + create_table :pending_statuses do |t| + t.references :account, null: false, foreign_key: { on_delete: :cascade } + t.references :fetch_account, null: false, foreign_key: { to_table: 'accounts', on_delete: :cascade } + t.string :uri, null: false, index: { unique: true } + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 88760ee1eaaa0f..649d9d4471f002 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_02_27_222450) do +ActiveRecord::Schema[7.1].define(version: 2024_02_27_225017) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1023,6 +1023,17 @@ t.index ["uri"], name: "index_pending_follow_requests_on_uri", unique: true end + create_table "pending_statuses", force: :cascade do |t| + t.bigint "account_id", null: false + t.bigint "fetch_account_id", null: false + t.string "uri", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_pending_statuses_on_account_id" + t.index ["fetch_account_id"], name: "index_pending_statuses_on_fetch_account_id" + t.index ["uri"], name: "index_pending_statuses_on_uri", unique: true + end + create_table "pghero_space_stats", force: :cascade do |t| t.text "database" t.text "schema" @@ -1617,6 +1628,8 @@ add_foreign_key "one_time_keys", "devices", on_delete: :cascade add_foreign_key "pending_follow_requests", "accounts", column: "target_account_id", on_delete: :cascade add_foreign_key "pending_follow_requests", "accounts", on_delete: :cascade + add_foreign_key "pending_statuses", "accounts", column: "fetch_account_id", on_delete: :cascade + add_foreign_key "pending_statuses", "accounts", on_delete: :cascade add_foreign_key "poll_votes", "accounts", on_delete: :cascade add_foreign_key "poll_votes", "polls", on_delete: :cascade add_foreign_key "polls", "accounts", on_delete: :cascade diff --git a/lib/tasks/dangerous.rake b/lib/tasks/dangerous.rake index 9d3e04a369f4a8..729eda9f358cdf 100644 --- a/lib/tasks/dangerous.rake +++ b/lib/tasks/dangerous.rake @@ -91,6 +91,7 @@ namespace :dangerous do 20240218233621 20240227033337 20240227222450 + 20240227225017 ) # Removed: account_groups target_tables = %w( @@ -111,6 +112,7 @@ namespace :dangerous do ng_rule_histories ngword_histories pending_follow_requests + pending_statuses scheduled_expiration_statuses status_capability_tokens status_references diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 1c50156b822404..f5137b9fe64b2a 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -2468,6 +2468,40 @@ def activity_for_object(json) end end + context 'when sender is in remote pending' do + subject { described_class.new(json, sender, delivery: true) } + + let!(:local_account) { Fabricate(:account) } + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + to: local_account ? ActivityPub::TagManager.instance.uri_for(local_account) : 'https://www.w3.org/ns/activitystreams#Public', + } + end + + before do + sender.update(suspended_at: Time.now.utc, suspension_origin: :local, remote_pending: true) + subject.perform + end + + it 'does not create a status' do + status = sender.statuses.first + + expect(status).to be_nil + end + + it 'pending data is created' do + pending = PendingStatus.find_by(account: sender) + + expect(pending).to_not be_nil + expect(pending.uri).to eq object_json[:id] + expect(pending.account_id).to eq sender.id + expect(pending.fetch_account_id).to eq local_account.id + end + end + context 'when sender is followed by local users' do subject { described_class.new(json, sender, delivery: true) } diff --git a/spec/services/enable_follow_requests_service_spec.rb b/spec/services/activate_follow_requests_service_spec.rb similarity index 96% rename from spec/services/enable_follow_requests_service_spec.rb rename to spec/services/activate_follow_requests_service_spec.rb index f4e93a32e16a0c..35acea157fa1f0 100644 --- a/spec/services/enable_follow_requests_service_spec.rb +++ b/spec/services/activate_follow_requests_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe EnableFollowRequestsService, type: :service do +RSpec.describe ActivateFollowRequestsService, type: :service do subject { described_class.new.call(sender) } let(:sender) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') }