mirror of
https://github.com/mastodon/mastodon.git
synced 2025-05-07 12:16:14 +00:00
277 lines
9.0 KiB
Ruby
277 lines
9.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
RSpec.describe ActivityPub::VerifyQuoteService do
|
|
subject { described_class.new }
|
|
|
|
let(:account) { Fabricate(:account, domain: 'a.example.com') }
|
|
let(:quoted_account) { Fabricate(:account, domain: 'b.example.com') }
|
|
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
|
let(:status) { Fabricate(:status, account: account) }
|
|
let(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri) }
|
|
|
|
context 'with an unfetchable approval URI' do
|
|
let(:approval_uri) { 'https://b.example.com/approvals/1234' }
|
|
|
|
before do
|
|
stub_request(:get, approval_uri)
|
|
.to_return(status: 404)
|
|
end
|
|
|
|
context 'with an already-fetched post' do
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('rejected')
|
|
end
|
|
end
|
|
|
|
context 'with an already-verified quote' do
|
|
let(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
|
|
|
it 'rejects the quote' do
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('revoked')
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with an approval URI' do
|
|
let(:approval_uri) { 'https://b.example.com/approvals/1234' }
|
|
|
|
let(:approval_type) { 'QuoteAuthorization' }
|
|
let(:approval_id) { approval_uri }
|
|
let(:approval_attributed_to) { ActivityPub::TagManager.instance.uri_for(quoted_account) }
|
|
let(:approval_interacting_object) { ActivityPub::TagManager.instance.uri_for(status) }
|
|
let(:approval_interaction_target) { ActivityPub::TagManager.instance.uri_for(quoted_status) }
|
|
|
|
let(:json) do
|
|
{
|
|
'@context': [
|
|
'https://www.w3.org/ns/activitystreams',
|
|
{
|
|
QuoteAuthorization: 'https://w3id.org/fep/044f#QuoteAuthorization',
|
|
gts: 'https://gotosocial.org/ns#',
|
|
interactionPolicy: {
|
|
'@id': 'gts:interactionPolicy',
|
|
'@type': '@id',
|
|
},
|
|
interactingObject: {
|
|
'@id': 'gts:interactingObject',
|
|
'@type': '@id',
|
|
},
|
|
interactionTarget: {
|
|
'@id': 'gts:interactionTarget',
|
|
'@type': '@id',
|
|
},
|
|
},
|
|
],
|
|
type: approval_type,
|
|
id: approval_id,
|
|
attributedTo: approval_attributed_to,
|
|
interactingObject: approval_interacting_object,
|
|
interactionTarget: approval_interaction_target,
|
|
}.with_indifferent_access
|
|
end
|
|
|
|
before do
|
|
stub_request(:get, approval_uri)
|
|
.to_return(status: 200, body: Oj.dump(json), headers: { 'Content-Type': 'application/activity+json' })
|
|
end
|
|
|
|
context 'with a valid activity for already-fetched posts' do
|
|
it 'updates the status' do
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('accepted')
|
|
|
|
expect(a_request(:get, approval_uri))
|
|
.to have_been_made.once
|
|
end
|
|
end
|
|
|
|
context 'with a valid activity for a post that cannot be fetched but is passed as fetched_quoted_object' do
|
|
let(:quoted_status) { nil }
|
|
|
|
let(:approval_interaction_target) { 'https://b.example.com/unknown-quoted' }
|
|
let(:prefetched_object) do
|
|
{
|
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
type: 'Note',
|
|
id: 'https://b.example.com/unknown-quoted',
|
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
|
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_account),
|
|
content: 'previously unknown post',
|
|
}.with_indifferent_access
|
|
end
|
|
|
|
before do
|
|
stub_request(:get, 'https://b.example.com/unknown-quoted')
|
|
.to_return(status: 404)
|
|
end
|
|
|
|
it 'updates the status' do
|
|
expect { subject.call(quote, fetchable_quoted_uri: 'https://b.example.com/unknown-quoted', prefetched_quoted_object: prefetched_object) }
|
|
.to change(quote, :state).to('accepted')
|
|
|
|
expect(a_request(:get, approval_uri))
|
|
.to have_been_made.once
|
|
|
|
expect(quote.reload.quoted_status.content).to eq 'previously unknown post'
|
|
end
|
|
end
|
|
|
|
context 'with a valid activity for a post that cannot be fetched but is inlined' do
|
|
let(:quoted_status) { nil }
|
|
|
|
let(:approval_interaction_target) do
|
|
{
|
|
type: 'Note',
|
|
id: 'https://b.example.com/unknown-quoted',
|
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
|
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_account),
|
|
content: 'previously unknown post',
|
|
}
|
|
end
|
|
|
|
before do
|
|
stub_request(:get, 'https://b.example.com/unknown-quoted')
|
|
.to_return(status: 404)
|
|
end
|
|
|
|
it 'updates the status' do
|
|
expect { subject.call(quote, fetchable_quoted_uri: 'https://b.example.com/unknown-quoted') }
|
|
.to change(quote, :state).to('accepted')
|
|
|
|
expect(a_request(:get, approval_uri))
|
|
.to have_been_made.once
|
|
|
|
expect(quote.reload.quoted_status.content).to eq 'previously unknown post'
|
|
end
|
|
end
|
|
|
|
context 'with a valid activity for a post that cannot be fetched and is inlined from an untrusted source' do
|
|
let(:quoted_status) { nil }
|
|
|
|
let(:approval_interaction_target) do
|
|
{
|
|
type: 'Note',
|
|
id: 'https://example.com/unknown-quoted',
|
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
|
attributedTo: ActivityPub::TagManager.instance.uri_for(account),
|
|
content: 'previously unknown post',
|
|
}
|
|
end
|
|
|
|
before do
|
|
stub_request(:get, 'https://example.com/unknown-quoted')
|
|
.to_return(status: 404)
|
|
end
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote, fetchable_quoted_uri: 'https://example.com/unknown-quoted') }
|
|
.to not_change(quote, :state)
|
|
.and not_change(quote, :quoted_status)
|
|
|
|
expect(a_request(:get, approval_uri))
|
|
.to have_been_made.once
|
|
end
|
|
end
|
|
|
|
context 'with a valid activity for already-fetched posts, with a pre-fetched approval' do
|
|
it 'updates the status without fetching the activity' do
|
|
expect { subject.call(quote, prefetched_approval: Oj.dump(json)) }
|
|
.to change(quote, :state).to('accepted')
|
|
|
|
expect(a_request(:get, approval_uri))
|
|
.to_not have_been_made
|
|
end
|
|
end
|
|
|
|
context 'with an unverifiable approval' do
|
|
let(:approval_uri) { 'https://evil.com/approvals/1234' }
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
|
|
context 'with an invalid approval document because of a mismatched ID' do
|
|
let(:approval_id) { 'https://evil.com/approvals/1234' }
|
|
|
|
it 'does not accept the quote' do
|
|
# NOTE: maybe we want to skip that instead of rejecting it?
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('rejected')
|
|
end
|
|
end
|
|
|
|
context 'with an approval from the wrong account' do
|
|
let(:approval_attributed_to) { ActivityPub::TagManager.instance.uri_for(Fabricate(:account, domain: 'b.example.com')) }
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
|
|
context 'with an approval for the wrong quoted post' do
|
|
let(:approval_interaction_target) { ActivityPub::TagManager.instance.uri_for(Fabricate(:status, account: quoted_account)) }
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
|
|
context 'with an approval for the wrong quote post' do
|
|
let(:approval_interacting_object) { ActivityPub::TagManager.instance.uri_for(Fabricate(:status, account: account)) }
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
|
|
context 'with an approval of the wrong type' do
|
|
let(:approval_type) { 'ReplyAuthorization' }
|
|
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with fast-track authorizations' do
|
|
let(:approval_uri) { nil }
|
|
|
|
context 'without any fast-track condition' do
|
|
it 'does not update the status' do
|
|
expect { subject.call(quote) }
|
|
.to_not change(quote, :state)
|
|
end
|
|
end
|
|
|
|
context 'when the account and the quoted account are the same' do
|
|
let(:quoted_account) { account }
|
|
|
|
it 'updates the status' do
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('accepted')
|
|
end
|
|
end
|
|
|
|
context 'when the account is mentioned by the quoted post' do
|
|
before do
|
|
quoted_status.mentions << Mention.new(account: account)
|
|
end
|
|
|
|
it 'updates the status' do
|
|
expect { subject.call(quote) }
|
|
.to change(quote, :state).to('accepted')
|
|
end
|
|
end
|
|
end
|
|
end
|