diff --git a/app/services/activitypub/account_backfill_service.rb b/app/services/activitypub/account_backfill_service.rb index e16de2d5b7..8be7e9ebcb 100644 --- a/app/services/activitypub/account_backfill_service.rb +++ b/app/services/activitypub/account_backfill_service.rb @@ -3,7 +3,7 @@ class ActivityPub::AccountBackfillService < BaseService include JsonLdHelper - ENABLED = (ENV['ACCOUNT_BACKFILL_ENABLED'] || true).to_bool + ENABLED = ENV['ACCOUNT_BACKFILL_ENABLED'].nil? || ENV['ACCOUNT_BACKFILL_ENABLED'] == 'true' MAX_STATUSES = (ENV['ACCOUNT_BACKFILL_MAX_STATUSES'] || 500).to_i MAX_PAGES = (ENV['ACCOUNT_BACKFILL_MAX_PAGES'] || 100).to_i @@ -14,6 +14,7 @@ class ActivityPub::AccountBackfillService < BaseService return if @account.nil? || @account.outbox_url.nil? @items, = collection_items(@account.outbox_url, max_items: MAX_STATUSES, max_pages: MAX_PAGES, on_behalf_of: on_behalf_of) + @items = filter_items(@items) return if @items.nil? on_behalf_of_id = on_behalf_of&.id @@ -30,4 +31,23 @@ class ActivityPub::AccountBackfillService < BaseService @items end + + private + + # Reject any non-public statuses. + # Since our request may have been signed on behalf of the follower, + # we may have received followers-only statuses. + # + # Formally, a followers-only status is addressed to the account's followers collection. + # We were not in that collection at the time that the post was made, + # so followers-only statuses fetched by backfilling are not addressed to us. + # Public and unlisted statuses are send to the activitystreams "Public" entity. + # We are part of the public, so those posts *are* addressed to us. + # + # @param items [Array] + # @return [Array] + def filter_items(items) + allowed = [:public, :unlisted] + items.filter { |item| item.is_a?(String) || allowed.include?(ActivityPub::Parser::StatusParser.new(item).visibility) } + end end diff --git a/spec/services/activitypub/account_backfill_service_spec.rb b/spec/services/activitypub/account_backfill_service_spec.rb index d79f57be78..ec98b960d3 100644 --- a/spec/services/activitypub/account_backfill_service_spec.rb +++ b/spec/services/activitypub/account_backfill_service_spec.rb @@ -5,11 +5,16 @@ require 'rails_helper' RSpec.describe ActivityPub::AccountBackfillService do subject { described_class.new } + before do + stub_const('ActivityPub::AccountBackfillService::ENABLED', true) + end + let!(:account) { Fabricate(:account, domain: 'other.com', outbox_url: 'http://other.com/alice/outbox') } let!(:outbox) do { '@context': 'https://www.w3.org/ns/activitystreams', + id: 'http://other.com/alice/outbox', type: 'OrderedCollection', first: 'http://other.com/alice/outbox?page=true', @@ -21,6 +26,8 @@ RSpec.describe ActivityPub::AccountBackfillService do { '@context': 'https://www.w3.org/ns/activitystreams', id: 'https://other.com/alice/1234', + to: ['https://www.w3.org/ns/activitystreams#Public'], + cc: ['https://other.com/alice/followers'], type: 'Note', content: 'Lorem ipsum', attributedTo: 'http://other.com/alice', @@ -51,5 +58,55 @@ RSpec.describe ActivityPub::AccountBackfillService do expect(got_items[1]).to eq(items[1]) expect(FetchReplyWorker).to have_received(:push_bulk).with([items[0].stringify_keys, items[1]]) end + + context 'with followers-only and private statuses' do + let!(:items) do + [ + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://other.com/alice/public', + type: 'Note', + to: ['https://www.w3.org/ns/activitystreams#Public'], + cc: ['https://other.com/alice/followers'], + content: 'Lorem ipsum', + attributedTo: 'http://other.com/alice', + }, + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://other.com/alice/unlisted', + to: ['https://other.com/alice/followers'], + cc: ['https://www.w3.org/ns/activitystreams#Public'], + type: 'Note', + content: 'Lorem ipsum', + attributedTo: 'http://other.com/alice', + }, + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://other.com/alice/followers-only', + to: ['https://other.com/alice/followers'], + type: 'Note', + content: 'Lorem ipsum', + attributedTo: 'http://other.com/alice', + }, + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://other.com/alice/dm', + to: ['https://other.com/alice/followers'], + type: 'Note', + content: 'Lorem ipsum', + attributedTo: 'http://other.com/alice', + }, + ] + end + + it 'only processes public and unlisted statuses' do + allow(FetchReplyWorker).to receive(:push_bulk) + got_items = subject.call(account) + expect(got_items.length).to eq(2) + expect(got_items[0].deep_symbolize_keys).to eq(items[0]) + expect(got_items[1].deep_symbolize_keys).to eq(items[1]) + expect(FetchReplyWorker).to have_received(:push_bulk).with([items[0].stringify_keys, items[1].stringify_keys]) + end + end end end