* Change: #375 投稿を編集して拡張ドメインブロックの条件にひっかかる状態になった場合、対象サーバーには投稿削除のActivityを送信する * Fix test * Add test
This commit is contained in:
parent
e4375143ca
commit
fcd13a6474
8 changed files with 146 additions and 12 deletions
|
@ -31,6 +31,10 @@ class StatusReachFinder
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inboxes_diff_for_sending_domain_block
|
||||||
|
(reached_account_inboxes_for_sending_domain_block + followers_inboxes_for_sending_domain_block).uniq
|
||||||
|
end
|
||||||
|
|
||||||
def all_inboxes
|
def all_inboxes
|
||||||
(inboxes + inboxes_for_misskey + inboxes_for_friend).uniq
|
(inboxes + inboxes_for_misskey + inboxes_for_friend).uniq
|
||||||
end
|
end
|
||||||
|
@ -58,6 +62,14 @@ class StatusReachFinder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reached_account_inboxes_for_sending_domain_block
|
||||||
|
if @status.reblog? || @status.limited_visibility?
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
Account.where(id: reached_account_ids, domain: banned_domains_of_status(@status)).inboxes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def reached_account_ids
|
def reached_account_ids
|
||||||
# When the status is a reblog, there are no interactions with it
|
# When the status is a reblog, there are no interactions with it
|
||||||
# directly, we assume all interactions are with the original one
|
# directly, we assume all interactions are with the original one
|
||||||
|
@ -144,6 +156,10 @@ class StatusReachFinder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def followers_inboxes_for_sending_domain_block
|
||||||
|
@status.account.followers.where(domain: banned_domains_of_status(@status)).inboxes
|
||||||
|
end
|
||||||
|
|
||||||
def relay_inboxes
|
def relay_inboxes
|
||||||
if @status.public_visibility?
|
if @status.public_visibility?
|
||||||
Relay.enabled.pluck(:inbox_url)
|
Relay.enabled.pluck(:inbox_url)
|
||||||
|
@ -192,9 +208,15 @@ class StatusReachFinder
|
||||||
end
|
end
|
||||||
|
|
||||||
def banned_domains_of_status(status)
|
def banned_domains_of_status(status)
|
||||||
|
return [] unless status.sending_sensitive?
|
||||||
|
return @banned_domains_of_status if defined?(@banned_domains_of_status) && status.id == @status.id
|
||||||
|
|
||||||
blocks = DomainBlock.where(domain: nil)
|
blocks = DomainBlock.where(domain: nil)
|
||||||
blocks = blocks.or(DomainBlock.where(reject_send_sensitive: true)) if (status.with_media? && status.sensitive) || status.spoiler_text?
|
blocks = blocks.or(DomainBlock.where(reject_send_sensitive: true)) if status.sending_sensitive?
|
||||||
blocks.pluck(:domain).uniq
|
domains = blocks.pluck(:domain).uniq
|
||||||
|
|
||||||
|
@banned_domains_of_status = domains if status.id == @status.id
|
||||||
|
domains
|
||||||
end
|
end
|
||||||
|
|
||||||
def banned_domains_for_misskey
|
def banned_domains_for_misskey
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Status::DomainBlockConcern
|
||||||
def sending_sensitive?
|
def sending_sensitive?
|
||||||
return false unless local?
|
return false unless local?
|
||||||
|
|
||||||
(with_media? && sensitive) || spoiler_text?
|
sensitive
|
||||||
end
|
end
|
||||||
|
|
||||||
def sending_maybe_compromised_privacy?
|
def sending_maybe_compromised_privacy?
|
||||||
|
|
|
@ -68,11 +68,7 @@ class PostStatusService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def preprocess_attributes!
|
def preprocess_attributes!
|
||||||
@sensitive = (if @options[:sensitive].nil?
|
@sensitive = (@options[:sensitive].nil? ? @account.user&.setting_default_sensitive : @options[:sensitive]) || @options[:spoiler_text].present?
|
||||||
@media.any? ? @account.user&.setting_default_sensitive : false
|
|
||||||
else
|
|
||||||
@options[:sensitive]
|
|
||||||
end) || @options[:spoiler_text].present?
|
|
||||||
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
|
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
|
||||||
@visibility = @options[:visibility]&.to_sym || @account.user&.setting_default_privacy&.to_sym
|
@visibility = @options[:visibility]&.to_sym || @account.user&.setting_default_privacy&.to_sym
|
||||||
@visibility = :limited if %w(mutual circle reply).include?(@options[:visibility])
|
@visibility = :limited if %w(mutual circle reply).include?(@options[:visibility])
|
||||||
|
|
|
@ -24,6 +24,7 @@ class UpdateStatusService < BaseService
|
||||||
@account_id = account_id
|
@account_id = account_id
|
||||||
@media_attachments_changed = false
|
@media_attachments_changed = false
|
||||||
@poll_changed = false
|
@poll_changed = false
|
||||||
|
@old_sensitive = sensitive?
|
||||||
|
|
||||||
clear_histories! if @options[:no_history]
|
clear_histories! if @options[:no_history]
|
||||||
|
|
||||||
|
@ -189,7 +190,7 @@ class UpdateStatusService < BaseService
|
||||||
|
|
||||||
def broadcast_updates!
|
def broadcast_updates!
|
||||||
DistributionWorker.perform_async(@status.id, { 'update' => true })
|
DistributionWorker.perform_async(@status.id, { 'update' => true })
|
||||||
ActivityPub::StatusUpdateDistributionWorker.perform_async(@status.id)
|
ActivityPub::StatusUpdateDistributionWorker.perform_async(@status.id, { 'sensitive' => sensitive?, 'sensitive_changed' => @old_sensitive != sensitive? && sensitive? })
|
||||||
end
|
end
|
||||||
|
|
||||||
def queue_poll_notifications!
|
def queue_poll_notifications!
|
||||||
|
@ -227,4 +228,8 @@ class UpdateStatusService < BaseService
|
||||||
@status.edited_at = nil
|
@status.edited_at = nil
|
||||||
@status.save!
|
@status.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sensitive?
|
||||||
|
@status.sensitive
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ class ActivityPub::StatusUpdateDistributionWorker < ActivityPub::DistributionWor
|
||||||
distribute_limited!
|
distribute_limited!
|
||||||
else
|
else
|
||||||
distribute!
|
distribute!
|
||||||
|
distribute_delete_activity!
|
||||||
end
|
end
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
true
|
true
|
||||||
|
@ -19,6 +20,17 @@ class ActivityPub::StatusUpdateDistributionWorker < ActivityPub::DistributionWor
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def inboxes
|
||||||
|
return super if @status.limited_visibility?
|
||||||
|
return super unless sensitive?
|
||||||
|
|
||||||
|
super - inboxes_diff_for_sending_domain_block
|
||||||
|
end
|
||||||
|
|
||||||
|
def inboxes_diff_for_sending_domain_block
|
||||||
|
status_reach_finder.inboxes_diff_for_sending_domain_block
|
||||||
|
end
|
||||||
|
|
||||||
def inboxes_for_limited
|
def inboxes_for_limited
|
||||||
@inboxes_for_limited ||= @status.mentioned_accounts.inboxes
|
@inboxes_for_limited ||= @status.mentioned_accounts.inboxes
|
||||||
end
|
end
|
||||||
|
@ -47,7 +59,30 @@ class ActivityPub::StatusUpdateDistributionWorker < ActivityPub::DistributionWor
|
||||||
build_activity(for_friend: true)
|
build_activity(for_friend: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_activity
|
||||||
|
@delete_activity ||= Oj.dump(serialize_payload(@status, ActivityPub::DeleteSerializer, signer: @account))
|
||||||
|
end
|
||||||
|
|
||||||
|
def distribute_delete_activity!
|
||||||
|
return unless sensitive_changed?
|
||||||
|
|
||||||
|
target_inboxes = inboxes_diff_for_sending_domain_block
|
||||||
|
return if target_inboxes.empty?
|
||||||
|
|
||||||
|
ActivityPub::DeliveryWorker.push_bulk(target_inboxes, limit: 1_000) do |inbox_url|
|
||||||
|
[delete_activity, @account.id, inbox_url, {}]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def always_sign
|
def always_sign
|
||||||
@status.limited_visibility?
|
@status.limited_visibility?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sensitive?
|
||||||
|
@options[:sensitive]
|
||||||
|
end
|
||||||
|
|
||||||
|
def sensitive_changed?
|
||||||
|
@options[:sensitive_changed]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -117,6 +117,33 @@ RSpec.describe ActivityPub::Activity::Update do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the status is not existing' do
|
||||||
|
let(:json) do
|
||||||
|
{
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
id: 'foo',
|
||||||
|
type: 'Update',
|
||||||
|
actor: sender.uri,
|
||||||
|
signature: 'foo',
|
||||||
|
object: {
|
||||||
|
type: 'Note',
|
||||||
|
id: 'https://example.com/note',
|
||||||
|
content: 'Ohagi is tsubuan',
|
||||||
|
},
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, 'https://example.com/inbox').to_return(status: 200)
|
||||||
|
subject.perform
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a new status', :sidekiq_inline do
|
||||||
|
status = Status.find_by(uri: 'https://example.com/note')
|
||||||
|
expect(status).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when the status is limited post and has conversation' do
|
context 'when the status is limited post and has conversation' do
|
||||||
let(:status) { Fabricate(:status, visibility: :limited, account: sender, uri: 'https://example.com/note', text: 'Ohagi is koshian') }
|
let(:status) { Fabricate(:status, visibility: :limited, account: sender, uri: 'https://example.com/note', text: 'Ohagi is koshian') }
|
||||||
let(:conversation) { Fabricate(:conversation, ancestor_status: status) }
|
let(:conversation) { Fabricate(:conversation, ancestor_status: status) }
|
||||||
|
|
|
@ -379,15 +379,15 @@ describe StatusReachFinder do
|
||||||
let(:visibility) { :public }
|
let(:visibility) { :public }
|
||||||
let(:searchability) { :public }
|
let(:searchability) { :public }
|
||||||
let(:dissubscribable) { false }
|
let(:dissubscribable) { false }
|
||||||
let(:spoiler_text) { '' }
|
let(:sensitive) { false }
|
||||||
let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, spoiler_text: spoiler_text) }
|
let(:status) { Fabricate(:status, account: alice, visibility: visibility, searchability: searchability, sensitive: sensitive) }
|
||||||
let(:alice) { Fabricate(:account, username: 'alice', master_settings: { subscription_policy: dissubscribable ? 'block' : 'allow' }) }
|
let(:alice) { Fabricate(:account, username: 'alice', master_settings: { subscription_policy: dissubscribable ? 'block' : 'allow' }) }
|
||||||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, uri: 'https://example.com/', inbox_url: 'https://example.com/inbox') }
|
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, uri: 'https://example.com/', inbox_url: 'https://example.com/inbox') }
|
||||||
let(:tom) { Fabricate(:account, username: 'tom', domain: 'tom.com', protocol: :activitypub, uri: 'https://tom.com/', inbox_url: 'https://tom.com/inbox') }
|
let(:tom) { Fabricate(:account, username: 'tom', domain: 'tom.com', protocol: :activitypub, uri: 'https://tom.com/', inbox_url: 'https://tom.com/inbox') }
|
||||||
|
|
||||||
context 'when reject_send_sensitive' do
|
context 'when reject_send_sensitive' do
|
||||||
let(:properties) { { reject_send_sensitive: true } }
|
let(:properties) { { reject_send_sensitive: true } }
|
||||||
let(:spoiler_text) { 'CW' }
|
let(:sensitive) { true }
|
||||||
|
|
||||||
it 'does not include the inbox of blocked domain' do
|
it 'does not include the inbox of blocked domain' do
|
||||||
expect(subject.inboxes).to_not include 'https://example.com/inbox'
|
expect(subject.inboxes).to_not include 'https://example.com/inbox'
|
||||||
|
|
|
@ -62,6 +62,55 @@ RSpec.describe UpdateStatusService, type: :service do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when content warning changes and has remote user', :sidekiq_inline do
|
||||||
|
let(:remote_follower) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor', protocol: :activitypub, inbox_url: 'https://example.com/inbox') }
|
||||||
|
let(:status) { Fabricate(:status, text: 'Foo', spoiler_text: '', account: Fabricate(:user).account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
remote_follower.follow!(status.account)
|
||||||
|
stub_request(:post, 'https://example.com/inbox').to_return(status: 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
def match_update_request(req, type)
|
||||||
|
json = JSON.parse(req.body)
|
||||||
|
actor_id = ActivityPub::TagManager.instance.uri_for(status.account)
|
||||||
|
status_id = ActivityPub::TagManager.instance.uri_for(status)
|
||||||
|
json['type'] == type && json['actor'] == actor_id && json['object']['id'] == status_id
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'edit activity is sent' do
|
||||||
|
subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar')
|
||||||
|
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Update') }).to have_been_made.once
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Delete') }).to_not have_been_made
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'edit activity is sent for target user' do
|
||||||
|
Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_send_sensitive: true)
|
||||||
|
subject.call(status, status.account_id, text: 'Ohagi')
|
||||||
|
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Update') }).to have_been_made.once
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Delete') }).to_not have_been_made
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'delete activity is sent when follower is target user' do
|
||||||
|
Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_send_sensitive: true)
|
||||||
|
subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar')
|
||||||
|
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Delete') }).to have_been_made.once
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Update') }).to_not have_been_made
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'delete activity is sent and update activity is not sent when follower is target user' do
|
||||||
|
Fabricate(:domain_block, domain: 'example.com', severity: :noop, reject_send_sensitive: true)
|
||||||
|
subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar')
|
||||||
|
subject.call(status, status.account_id, text: 'Ohagi', spoiler_text: 'Bar')
|
||||||
|
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Delete') }).to have_been_made.once
|
||||||
|
expect(a_request(:post, 'https://example.com/inbox').with { |req| match_update_request(req, 'Update') }).to_not have_been_made
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when media attachments change' do
|
context 'when media attachments change' do
|
||||||
let!(:status) { Fabricate(:status, text: 'Foo') }
|
let!(:status) { Fabricate(:status, text: 'Foo') }
|
||||||
let!(:detached_media_attachment) { Fabricate(:media_attachment, account: status.account) }
|
let!(:detached_media_attachment) { Fabricate(:media_attachment, account: status.account) }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue