Handle Accept of a FeatureRequest (#38251)

This commit is contained in:
David Roetzel
2026-03-17 12:45:00 +01:00
committed by GitHub
parent 68984de5b5
commit 5f36c482d2
2 changed files with 84 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
return accept_follow_for_relay if relay_follow?
return accept_follow!(follow_request_from_object) unless follow_request_from_object.nil?
return accept_quote!(quote_request_from_object) unless quote_request_from_object.nil?
return accept_feature_request! if Mastodon::Feature.collections_federation_enabled? && feature_request_from_object.present?
case @object['type']
when 'Follow'
@@ -44,6 +45,17 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
accept_quote!(quote)
end
def accept_feature_request!
approval_uri = value_or_id(first_of_value(@json['result']))
return if approval_uri.nil? || unsupported_uri_scheme?(approval_uri) || non_matching_uri_hosts?(approval_uri, @account.uri)
collection_item = feature_request_from_object
collection_item.update!(approval_uri:, state: :accepted)
activity_json = ActiveModelSerializers::SerializableResource.new(collection_item, serializer: ActivityPub::AddFeaturedItemSerializer, adapter: ActivityPub::Adapter).to_json
ActivityPub::AccountRawDistributionWorker.perform_async(activity_json, collection_item.collection.account_id)
end
def accept_quote!(quote)
approval_uri = value_or_id(first_of_value(@json['result']))
return if unsupported_uri_scheme?(approval_uri) || quote.quoted_account != @account || !quote.status.local? || !quote.pending?
@@ -72,4 +84,10 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
def target_uri
@target_uri ||= value_or_id(@object['actor'])
end
def feature_request_from_object
return @collection_item if instance_variable_defined?(:@collection_item)
@collection_item = CollectionItem.local.find_by(activity_uri: value_or_id(@object), account_id: @account.id)
end
end

View File

@@ -171,5 +171,71 @@ RSpec.describe ActivityPub::Activity::Accept do
end
end
end
context 'with a FeatureRequest', feature: :collections_federation do
let(:collection) { Fabricate(:collection, account: recipient) }
let(:collection_item) { Fabricate(:collection_item, collection:, account: sender, state: :pending) }
let(:object) { collection_item.activity_uri }
let(:approval_uri) { 'https://example.com/stamps/1' }
let(:json) do
{
'id' => 'https://example.com/accepts/1',
'type' => 'Accept',
'actor' => sender.uri,
'to' => ActivityPub::TagManager.instance.uri_for(recipient),
'object' => object,
'result' => approval_uri,
}
end
context 'when activity is valid' do
it 'accepts the collection item, stores the authorization uri and federates an `Add` activity' do
subject.perform
expect(collection_item.reload).to be_accepted
expect(collection_item.approval_uri).to eq 'https://example.com/stamps/1'
expect(ActivityPub::AccountRawDistributionWorker)
.to have_enqueued_sidekiq_job
end
end
context 'when activity is invalid' do
shared_examples 'ignoring activity' do
it 'does not accept the item and does not send out an activity' do
subject.perform
expect(collection_item.reload).to_not be_accepted
expect(collection_item.approval_uri).to be_nil
expect(ActivityPub::AccountRawDistributionWorker)
.to_not have_enqueued_sidekiq_job
end
end
context 'when matching collection item cannot be found' do
let(:object) { 'https://localhost/feature_requests/1' }
it_behaves_like 'ignoring activity'
end
context 'when the sender is not the featured account' do
let(:other_account) { Fabricate(:remote_account) }
let(:collection_item) { Fabricate(:collection_item, collection:, account: other_account, state: :pending) }
it_behaves_like 'ignoring activity'
end
context "when approval_uri does not match the sender's uri" do
let(:approval_uri) { 'https://other.localhost/authorizations/1' }
it_behaves_like 'ignoring activity'
end
context 'when approval_uri is missing' do
let(:approval_uri) { nil }
it_behaves_like 'ignoring activity'
end
end
end
end
end