From 6d15ba8311f1571e820189a4d5f37d4562824185 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 4 Apr 2025 16:38:29 +0200 Subject: [PATCH] [WiP] Handle quote revocations --- app/lib/activitypub/activity/delete.rb | 43 ++++++++++----- spec/lib/activitypub/activity/delete_spec.rb | 57 ++++++++++++++++++++ 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/app/lib/activitypub/activity/delete.rb b/app/lib/activitypub/activity/delete.rb index 61f6ca699..69b7bd035 100644 --- a/app/lib/activitypub/activity/delete.rb +++ b/app/lib/activitypub/activity/delete.rb @@ -5,7 +5,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity if @account.uri == object_uri delete_person else - delete_note + delete_object end end @@ -17,7 +17,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity end end - def delete_note + def delete_object return if object_uri.nil? with_redis_lock("delete_status_in_progress:#{object_uri}", raise_on_failure: false) do @@ -32,21 +32,38 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity Tombstone.find_or_create_by(uri: object_uri, account: @account) end - @status = Status.find_by(uri: object_uri, account: @account) - @status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present? - - return if @status.nil? - - forwarder.forward! if forwarder.forwardable? - delete_now! + case @object['type'] + when 'QuoteAuthorization' + revoke_quote + when 'Note', 'Question' + delete_status + else + delete_status || revoke_quote + end end end + def delete_status + @status = Status.find_by(uri: object_uri, account: @account) + @status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present? + + return if @status.nil? + + forwarder.forward! if forwarder.forwardable? + RemoveStatusService.new.call(@status, redraft: false) + + true + end + + def revoke_quote + @quote = Quote.find_by(approval_uri: object_uri, quoted_account: @account) + return if @quote.nil? + + ActivityPub::Forwarder.new(@account, @json, @quote.status).forward! + @quote.reject! + end + def forwarder @forwarder ||= ActivityPub::Forwarder.new(@account, @json, @status) end - - def delete_now! - RemoveStatusService.new.call(@status, redraft: false) - end end diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb index 71977a96a..849c7ada9 100644 --- a/spec/lib/activitypub/activity/delete_spec.rb +++ b/spec/lib/activitypub/activity/delete_spec.rb @@ -77,4 +77,61 @@ RSpec.describe ActivityPub::Activity::Delete do end end end + + context 'when the deleted object is an account' do + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Delete', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: ActivityPub::TagManager.instance.uri_for(sender), + signature: 'foo', + }.with_indifferent_access + end + + describe '#perform' do + subject { described_class.new(json, sender) } + + let(:service) { instance_double(DeleteAccountService, call: true) } + + before do + allow(DeleteAccountService).to receive(:new).and_return(service) + end + + it 'calls the account deletion service' do + subject.perform + + expect(service) + .to have_received(:call).with(sender, { reserve_username: false, skip_activitypub: true }) + end + end + end + + context 'when the deleted object is a quote authorization' do + let(:quoter) { Fabricate(:account, domain: 'b.example.com') } + let(:status) { Fabricate(:status, account: quoter) } + let(:quoted_status) { Fabricate(:status, account: sender, uri: 'https://example.com/statuses/1234') } + let!(:quote) { Fabricate(:quote, approval_uri: 'https://example.com/approvals/1234', state: :accepted, status: status, quoted_status: quoted_status) } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Delete', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: quote.approval_uri, + signature: 'foo', + }.with_indifferent_access + end + + describe '#perform' do + subject { described_class.new(json, sender) } + + it 'revokes the authorization' do + expect { subject.perform } + .to change { quote.reload.state }.to('revoked') + end + end + end end