Merge commit '96a96a79caeaddf4ce07c3d0468332902b00eab2' into glitch-soc/merge-4.5
This commit is contained in:
@@ -47,7 +47,7 @@ class Api::Fasp::BaseController < ApplicationController
|
|||||||
provider = nil
|
provider = nil
|
||||||
|
|
||||||
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
|
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
|
||||||
provider = Fasp::Provider.find(keyid)
|
provider = Fasp::Provider.confirmed.find(keyid)
|
||||||
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ module JsonLdHelper
|
|||||||
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
|
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def supported_security_context?(json)
|
||||||
|
!json.nil? && equals_or_includes?(json['@context'], 'https://w3id.org/security/v1')
|
||||||
|
end
|
||||||
|
|
||||||
def unsupported_uri_scheme?(uri)
|
def unsupported_uri_scheme?(uri)
|
||||||
uri.nil? || !uri.start_with?('http://', 'https://')
|
uri.nil? || !uri.start_with?('http://', 'https://')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function fetchStatusFail(id, error, skipLoading, parentQuotePostId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function redraft(status, raw_text) {
|
export function redraft(status, raw_text, quoted_status_id = null) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
|
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
|
||||||
|
|
||||||
@@ -117,6 +117,7 @@ export function redraft(status, raw_text) {
|
|||||||
type: REDRAFT,
|
type: REDRAFT,
|
||||||
status,
|
status,
|
||||||
raw_text,
|
raw_text,
|
||||||
|
quoted_status_id,
|
||||||
maxOptions,
|
maxOptions,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -169,7 +170,7 @@ export function deleteStatus(id, withRedraft = false) {
|
|||||||
dispatch(importFetchedAccount(response.data.account));
|
dispatch(importFetchedAccount(response.data.account));
|
||||||
|
|
||||||
if (withRedraft) {
|
if (withRedraft) {
|
||||||
dispatch(redraft(status, response.data.text));
|
dispatch(redraft(status, response.data.text, response.data.quote?.quoted_status?.id));
|
||||||
ensureComposeIsVisible(getState);
|
ensureComposeIsVisible(getState);
|
||||||
} else {
|
} else {
|
||||||
dispatch(showAlert({ message: messages.deleteSuccess }));
|
dispatch(showAlert({ message: messages.deleteSuccess }));
|
||||||
|
|||||||
@@ -21,10 +21,19 @@ export async function importEmojiData(localeString: string, path?: string) {
|
|||||||
path ??= await localeToPath(locale);
|
path ??= await localeToPath(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix from #37858. Check if we've loaded this path before.
|
||||||
|
const existing = await loadLatestEtag(locale);
|
||||||
|
if (existing === path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale, path);
|
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale, path);
|
||||||
if (!emojis) {
|
if (!emojis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await putLatestEtag(path, locale); // Fix from #37858. Put the path as the ETag to ensure we don't load the same data again.
|
||||||
|
|
||||||
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
||||||
await putEmojiData(flattenedEmojis, locale);
|
await putEmojiData(flattenedEmojis, locale);
|
||||||
return flattenedEmojis;
|
return flattenedEmojis;
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ export const composeReducer = (state = initialState, action) => {
|
|||||||
map.set('sensitive', action.status.get('sensitive'));
|
map.set('sensitive', action.status.get('sensitive'));
|
||||||
map.set('language', action.status.get('language'));
|
map.set('language', action.status.get('language'));
|
||||||
map.set('id', null);
|
map.set('id', null);
|
||||||
map.set('quoted_status_id', action.status.getIn(['quote', 'quoted_status'], null));
|
map.set('quoted_status_id', action.quoted_status_id);
|
||||||
// Mastodon-authored posts can be expected to have at most one automatic approval policy
|
// Mastodon-authored posts can be expected to have at most one automatic approval policy
|
||||||
map.set('quote_policy', action.status.getIn(['quote_approval', 'automatic', 0]) || 'nobody');
|
map.set('quote_policy', action.status.getIn(['quote_approval', 'automatic', 0]) || 'nobody');
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class Fasp::Request
|
|||||||
response = HTTP
|
response = HTTP
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.use(http_signature: { key:, covered_components: COVERED_COMPONENTS })
|
.use(http_signature: { key:, covered_components: COVERED_COMPONENTS })
|
||||||
.send(verb, url, body:)
|
.send(verb, url, body:, socket_class: ::Request::Socket)
|
||||||
|
|
||||||
validate!(response)
|
validate!(response)
|
||||||
@provider.delivery_failure_tracker.track_success!
|
@provider.delivery_failure_tracker.track_success!
|
||||||
|
|||||||
@@ -349,5 +349,5 @@ class Request
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private_constant :ClientLimit, :Socket, :ProxySocket
|
private_constant :ClientLimit
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class Fasp::Provider < ApplicationRecord
|
|||||||
before_create :create_keypair
|
before_create :create_keypair
|
||||||
after_commit :update_remote_capabilities
|
after_commit :update_remote_capabilities
|
||||||
|
|
||||||
|
scope :confirmed, -> { where(confirmed: true) }
|
||||||
scope :with_capability, lambda { |capability_name|
|
scope :with_capability, lambda { |capability_name|
|
||||||
where('fasp_providers.capabilities @> ?::jsonb', "[{\"id\": \"#{capability_name}\", \"enabled\": true}]")
|
where('fasp_providers.capabilities @> ?::jsonb', "[{\"id\": \"#{capability_name}\", \"enabled\": true}]")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class REST::BaseQuoteSerializer < ActiveModel::Serializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def quoted_status
|
def quoted_status
|
||||||
object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized'
|
object.quoted_status if (object.accepted? || instance_options[:source_requested]) && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService
|
|||||||
@json = fetch_resource(uri, false)
|
@json = fetch_resource(uri, false)
|
||||||
|
|
||||||
raise Error, "Unable to fetch key JSON at #{uri}" if @json.nil?
|
raise Error, "Unable to fetch key JSON at #{uri}" if @json.nil?
|
||||||
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?(@json)
|
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?(@json) || (supported_security_context?(@json) && @json['owner'].present? && !actor_type?)
|
||||||
raise Error, "Unexpected object type for key #{uri}" unless expected_type?
|
raise Error, "Unexpected object type for key #{uri}" unless expected_type?
|
||||||
return find_actor(@json['id'], @json, suppress_errors) if actor_type?
|
return find_actor(@json['id'], @json, suppress_errors) if actor_type?
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
|||||||
Tag.find_or_create_by_names([tag]).filter(&:valid?)
|
Tag.find_or_create_by_names([tag]).filter(&:valid?)
|
||||||
rescue ActiveRecord::RecordInvalid
|
rescue ActiveRecord::RecordInvalid
|
||||||
[]
|
[]
|
||||||
end
|
end.uniq
|
||||||
|
|
||||||
return unless @status.distributable?
|
return unless @status.distributable?
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,12 @@ class BlockDomainService < BaseService
|
|||||||
suspend_accounts!
|
suspend_accounts!
|
||||||
end
|
end
|
||||||
|
|
||||||
DomainClearMediaWorker.perform_async(domain_block.id) if domain_block.reject_media?
|
if domain_block.suspend?
|
||||||
|
# Account images and attachments are already handled by `suspend_accounts!`
|
||||||
|
PurgeCustomEmojiWorker.perform_async(blocked_domain)
|
||||||
|
elsif domain_block.reject_media?
|
||||||
|
DomainClearMediaWorker.perform_async(domain_block.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def silence_accounts!
|
def silence_accounts!
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class Fasp::BaseWorker
|
|||||||
private
|
private
|
||||||
|
|
||||||
def with_provider(provider)
|
def with_provider(provider)
|
||||||
return unless provider.available?
|
return unless provider.confirmed? && provider.available?
|
||||||
|
|
||||||
yield
|
yield
|
||||||
rescue *Mastodon::HTTP_CONNECTION_ERRORS
|
rescue *Mastodon::HTTP_CONNECTION_ERRORS
|
||||||
|
|||||||
15
app/workers/purge_custom_emoji_worker.rb
Normal file
15
app/workers/purge_custom_emoji_worker.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PurgeCustomEmojiWorker
|
||||||
|
include Sidekiq::IterableJob
|
||||||
|
|
||||||
|
def build_enumerator(domain, cursor:)
|
||||||
|
return if domain.blank?
|
||||||
|
|
||||||
|
active_record_batches_enumerator(CustomEmoji.by_domain_and_subdomains(domain), cursor:)
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_iteration(custom_emojis, _domain)
|
||||||
|
AttachmentBatch.new(CustomEmoji, custom_emojis).delete
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -109,15 +109,27 @@ module Mastodon::CLI
|
|||||||
end
|
end
|
||||||
|
|
||||||
option :remote_only, type: :boolean
|
option :remote_only, type: :boolean
|
||||||
|
option :suspended_only, type: :boolean
|
||||||
desc 'purge', 'Remove all custom emoji'
|
desc 'purge', 'Remove all custom emoji'
|
||||||
long_desc <<-LONG_DESC
|
long_desc <<-LONG_DESC
|
||||||
Removes all custom emoji.
|
Removes all custom emoji.
|
||||||
|
|
||||||
With the --remote-only option, only remote emoji will be deleted.
|
With the --remote-only option, only remote emoji will be deleted.
|
||||||
|
|
||||||
|
With the --suspended-only option, only emoji from suspended servers will be deleted.
|
||||||
LONG_DESC
|
LONG_DESC
|
||||||
def purge
|
def purge
|
||||||
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
|
if options[:suspended_only]
|
||||||
scope.in_batches.destroy_all
|
DomainBlock.where(severity: :suspend).find_each do |domain_block|
|
||||||
|
CustomEmoji.by_domain_and_subdomains(domain_block.domain).find_in_batches do |custom_emojis|
|
||||||
|
AttachmentBatch.new(CustomEmoji, custom_emojis).delete
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
|
||||||
|
scope.in_batches.destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
say('OK', :green)
|
say('OK', :green)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,24 @@ RSpec.describe Fasp::Request do
|
|||||||
expect(provider.delivery_failure_tracker.failures).to eq 1
|
expect(provider.delivery_failure_tracker.failures).to eq 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the provider host name resolves to a private address' do
|
||||||
|
around do |example|
|
||||||
|
WebMock.disable!
|
||||||
|
example.run
|
||||||
|
WebMock.enable!
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises Mastodon::ValidationError' do
|
||||||
|
resolver = instance_double(Resolv::DNS)
|
||||||
|
|
||||||
|
allow(resolver).to receive(:getaddresses).with('reqprov.example.com').and_return(%w(0.0.0.0 2001:db8::face))
|
||||||
|
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||||
|
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||||
|
|
||||||
|
expect { subject.send(method, '/test_path') }.to raise_error(Mastodon::ValidationError)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#get' do
|
describe '#get' do
|
||||||
|
|||||||
@@ -23,6 +23,35 @@ RSpec.describe Mastodon::CLI::Emoji do
|
|||||||
.to output_results('OK')
|
.to output_results('OK')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with --suspended-only and existing custom emoji on blocked servers' do
|
||||||
|
let(:blocked_domain) { 'evil.com' }
|
||||||
|
let(:blocked_subdomain) { 'subdomain.evil.org' }
|
||||||
|
let(:blocked_domain_without_emoji) { 'blocked.com' }
|
||||||
|
let(:silenced_domain) { 'silenced.com' }
|
||||||
|
|
||||||
|
let(:options) { { suspended_only: true } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Fabricate(:custom_emoji)
|
||||||
|
Fabricate(:custom_emoji, domain: blocked_domain)
|
||||||
|
Fabricate(:custom_emoji, domain: blocked_subdomain)
|
||||||
|
Fabricate(:custom_emoji, domain: silenced_domain)
|
||||||
|
|
||||||
|
Fabricate(:domain_block, severity: :suspend, domain: blocked_domain)
|
||||||
|
Fabricate(:domain_block, severity: :suspend, domain: 'evil.org')
|
||||||
|
Fabricate(:domain_block, severity: :suspend, domain: blocked_domain_without_emoji)
|
||||||
|
Fabricate(:domain_block, severity: :silence, domain: silenced_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reports a successful purge' do
|
||||||
|
expect { subject }
|
||||||
|
.to change { CustomEmoji.by_domain_and_subdomains(blocked_domain).count }.to(0)
|
||||||
|
.and change { CustomEmoji.by_domain_and_subdomains('evil.org').count }.to(0)
|
||||||
|
.and not_change { CustomEmoji.by_domain_and_subdomains(silenced_domain).count }
|
||||||
|
.and(not_change { CustomEmoji.local.count })
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#import' do
|
describe '#import' do
|
||||||
|
|||||||
@@ -6,34 +6,33 @@ RSpec.describe 'Api::Fasp::DataSharing::V0::BackfillRequests', feature: :fasp do
|
|||||||
include ProviderRequestHelper
|
include ProviderRequestHelper
|
||||||
|
|
||||||
describe 'POST /api/fasp/data_sharing/v0/backfill_requests' do
|
describe 'POST /api/fasp/data_sharing/v0/backfill_requests' do
|
||||||
let(:provider) { Fabricate(:fasp_provider) }
|
subject do
|
||||||
|
post api_fasp_data_sharing_v0_backfill_requests_path, headers:, params:, as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
|
let(:params) { { category: 'content', maxCount: 10 } }
|
||||||
|
let(:headers) do
|
||||||
|
request_authentication_headers(provider,
|
||||||
|
url: api_fasp_data_sharing_v0_backfill_requests_url,
|
||||||
|
method: :post,
|
||||||
|
body: params)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for unconfirmed provider'
|
||||||
|
|
||||||
context 'with valid parameters' do
|
context 'with valid parameters' do
|
||||||
it 'creates a new backfill request' do
|
it 'creates a new backfill request' do
|
||||||
params = { category: 'content', maxCount: 10 }
|
expect { subject }.to change(Fasp::BackfillRequest, :count).by(1)
|
||||||
headers = request_authentication_headers(provider,
|
|
||||||
url: api_fasp_data_sharing_v0_backfill_requests_url,
|
|
||||||
method: :post,
|
|
||||||
body: params)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
post api_fasp_data_sharing_v0_backfill_requests_path, headers:, params:, as: :json
|
|
||||||
end.to change(Fasp::BackfillRequest, :count).by(1)
|
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with invalid parameters' do
|
context 'with invalid parameters' do
|
||||||
it 'does not create a backfill request' do
|
let(:params) { { category: 'unknown', maxCount: 10 } }
|
||||||
params = { category: 'unknown', maxCount: 10 }
|
|
||||||
headers = request_authentication_headers(provider,
|
|
||||||
url: api_fasp_data_sharing_v0_backfill_requests_url,
|
|
||||||
method: :post,
|
|
||||||
body: params)
|
|
||||||
|
|
||||||
expect do
|
it 'does not create a backfill request' do
|
||||||
post api_fasp_data_sharing_v0_backfill_requests_path, headers:, params:, as: :json
|
expect { subject }.to_not change(Fasp::BackfillRequest, :count)
|
||||||
end.to_not change(Fasp::BackfillRequest, :count)
|
|
||||||
expect(response).to have_http_status(422)
|
expect(response).to have_http_status(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,15 +6,22 @@ RSpec.describe 'Api::Fasp::DataSharing::V0::Continuations', feature: :fasp do
|
|||||||
include ProviderRequestHelper
|
include ProviderRequestHelper
|
||||||
|
|
||||||
describe 'POST /api/fasp/data_sharing/v0/backfill_requests/:id/continuations' do
|
describe 'POST /api/fasp/data_sharing/v0/backfill_requests/:id/continuations' do
|
||||||
let(:backfill_request) { Fabricate(:fasp_backfill_request) }
|
subject do
|
||||||
let(:provider) { backfill_request.fasp_provider }
|
post api_fasp_data_sharing_v0_backfill_request_continuation_path(backfill_request), headers:, as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
|
let(:backfill_request) { Fabricate(:fasp_backfill_request, fasp_provider: provider) }
|
||||||
|
let(:headers) do
|
||||||
|
request_authentication_headers(provider,
|
||||||
|
url: api_fasp_data_sharing_v0_backfill_request_continuation_url(backfill_request),
|
||||||
|
method: :post)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for unconfirmed provider'
|
||||||
|
|
||||||
it 'queues a job to continue the given backfill request' do
|
it 'queues a job to continue the given backfill request' do
|
||||||
headers = request_authentication_headers(provider,
|
subject
|
||||||
url: api_fasp_data_sharing_v0_backfill_request_continuation_url(backfill_request),
|
|
||||||
method: :post)
|
|
||||||
|
|
||||||
post api_fasp_data_sharing_v0_backfill_request_continuation_path(backfill_request), headers:, as: :json
|
|
||||||
expect(response).to have_http_status(204)
|
expect(response).to have_http_status(204)
|
||||||
expect(Fasp::BackfillWorker).to have_enqueued_sidekiq_job(backfill_request.id)
|
expect(Fasp::BackfillWorker).to have_enqueued_sidekiq_job(backfill_request.id)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,51 +6,57 @@ RSpec.describe 'Api::Fasp::DataSharing::V0::EventSubscriptions', feature: :fasp
|
|||||||
include ProviderRequestHelper
|
include ProviderRequestHelper
|
||||||
|
|
||||||
describe 'POST /api/fasp/data_sharing/v0/event_subscriptions' do
|
describe 'POST /api/fasp/data_sharing/v0/event_subscriptions' do
|
||||||
let(:provider) { Fabricate(:fasp_provider) }
|
subject do
|
||||||
|
post api_fasp_data_sharing_v0_event_subscriptions_path, headers:, params:, as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
|
let(:params) { { category: 'content', subscriptionType: 'lifecycle', maxBatchSize: 10 } }
|
||||||
|
let(:headers) do
|
||||||
|
request_authentication_headers(provider,
|
||||||
|
url: api_fasp_data_sharing_v0_event_subscriptions_url,
|
||||||
|
method: :post,
|
||||||
|
body: params)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for unconfirmed provider'
|
||||||
|
|
||||||
context 'with valid parameters' do
|
context 'with valid parameters' do
|
||||||
it 'creates a new subscription' do
|
it 'creates a new subscription' do
|
||||||
params = { category: 'content', subscriptionType: 'lifecycle', maxBatchSize: 10 }
|
|
||||||
headers = request_authentication_headers(provider,
|
|
||||||
url: api_fasp_data_sharing_v0_event_subscriptions_url,
|
|
||||||
method: :post,
|
|
||||||
body: params)
|
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
post api_fasp_data_sharing_v0_event_subscriptions_path, headers:, params:, as: :json
|
subject
|
||||||
end.to change(Fasp::Subscription, :count).by(1)
|
end.to change(Fasp::Subscription, :count).by(1)
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with invalid parameters' do
|
context 'with invalid parameters' do
|
||||||
it 'does not create a subscription' do
|
let(:params) { { category: 'unknown' } }
|
||||||
params = { category: 'unknown' }
|
|
||||||
headers = request_authentication_headers(provider,
|
|
||||||
url: api_fasp_data_sharing_v0_event_subscriptions_url,
|
|
||||||
method: :post,
|
|
||||||
body: params)
|
|
||||||
|
|
||||||
expect do
|
it 'does not create a subscription' do
|
||||||
post api_fasp_data_sharing_v0_event_subscriptions_path, headers:, params:, as: :json
|
expect { subject }.to_not change(Fasp::Subscription, :count)
|
||||||
end.to_not change(Fasp::Subscription, :count)
|
|
||||||
expect(response).to have_http_status(422)
|
expect(response).to have_http_status(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'DELETE /api/fasp/data_sharing/v0/event_subscriptions/:id' do
|
describe 'DELETE /api/fasp/data_sharing/v0/event_subscriptions/:id' do
|
||||||
let(:subscription) { Fabricate(:fasp_subscription) }
|
subject do
|
||||||
let(:provider) { subscription.fasp_provider }
|
delete api_fasp_data_sharing_v0_event_subscription_path(subscription), headers:, as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
|
let!(:subscription) { Fabricate(:fasp_subscription, fasp_provider: provider) }
|
||||||
|
let(:headers) do
|
||||||
|
request_authentication_headers(provider,
|
||||||
|
url: api_fasp_data_sharing_v0_event_subscription_url(subscription),
|
||||||
|
method: :delete)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for unconfirmed provider'
|
||||||
|
|
||||||
it 'deletes the subscription' do
|
it 'deletes the subscription' do
|
||||||
headers = request_authentication_headers(provider,
|
expect { subject }.to change(Fasp::Subscription, :count).by(-1)
|
||||||
url: api_fasp_data_sharing_v0_event_subscription_url(subscription),
|
|
||||||
method: :delete)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
delete api_fasp_data_sharing_v0_event_subscription_path(subscription), headers:, as: :json
|
|
||||||
end.to change(Fasp::Subscription, :count).by(-1)
|
|
||||||
expect(response).to have_http_status(204)
|
expect(response).to have_http_status(204)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,18 +6,23 @@ RSpec.describe 'Api::Fasp::Debug::V0::Callback::Responses', feature: :fasp do
|
|||||||
include ProviderRequestHelper
|
include ProviderRequestHelper
|
||||||
|
|
||||||
describe 'POST /api/fasp/debug/v0/callback/responses' do
|
describe 'POST /api/fasp/debug/v0/callback/responses' do
|
||||||
let(:provider) { Fabricate(:debug_fasp) }
|
subject do
|
||||||
|
post api_fasp_debug_v0_callback_responses_path, headers:, params: payload, as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
|
let(:payload) { { test: 'call' } }
|
||||||
|
let(:headers) do
|
||||||
|
request_authentication_headers(provider,
|
||||||
|
url: api_fasp_debug_v0_callback_responses_url,
|
||||||
|
method: :post,
|
||||||
|
body: payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for unconfirmed provider'
|
||||||
|
|
||||||
it 'create a record of the callback' do
|
it 'create a record of the callback' do
|
||||||
payload = { test: 'call' }
|
expect { subject }.to change(Fasp::DebugCallback, :count).by(1)
|
||||||
headers = request_authentication_headers(provider,
|
|
||||||
url: api_fasp_debug_v0_callback_responses_url,
|
|
||||||
method: :post,
|
|
||||||
body: payload)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
post api_fasp_debug_v0_callback_responses_path, headers:, params: payload, as: :json
|
|
||||||
end.to change(Fasp::DebugCallback, :count).by(1)
|
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
|
|
||||||
debug_callback = Fasp::DebugCallback.last
|
debug_callback = Fasp::DebugCallback.last
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService do
|
|||||||
let(:public_key_id) { 'https://example.com/alice-public-key.json' }
|
let(:public_key_id) { 'https://example.com/alice-public-key.json' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' })
|
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the expected account' do
|
it 'returns the expected account' do
|
||||||
|
|||||||
@@ -259,6 +259,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
|
|||||||
{ type: 'Hashtag', name: 'foo' },
|
{ type: 'Hashtag', name: 'foo' },
|
||||||
{ type: 'Hashtag', name: 'bar' },
|
{ type: 'Hashtag', name: 'bar' },
|
||||||
{ type: 'Hashtag', name: '#2024' },
|
{ type: 'Hashtag', name: '#2024' },
|
||||||
|
{ type: 'Hashtag', name: 'Foo Bar' },
|
||||||
|
{ type: 'Hashtag', name: 'FooBar' },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -270,7 +272,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
|
|||||||
|
|
||||||
it 'updates tags and featured tags' do
|
it 'updates tags and featured tags' do
|
||||||
expect { subject.call(status, json, json) }
|
expect { subject.call(status, json, json) }
|
||||||
.to change { status.tags.reload.pluck(:name) }.from(contain_exactly('test', 'foo')).to(contain_exactly('foo', 'bar'))
|
.to change { status.tags.reload.pluck(:name) }.from(contain_exactly('test', 'foo')).to(contain_exactly('foo', 'bar', 'foobar'))
|
||||||
.and change { status.account.featured_tags.find_by(name: 'test').statuses_count }.by(-1)
|
.and change { status.account.featured_tags.find_by(name: 'test').statuses_count }.by(-1)
|
||||||
.and change { status.account.featured_tags.find_by(name: 'bar').statuses_count }.by(1)
|
.and change { status.account.featured_tags.find_by(name: 'bar').statuses_count }.by(1)
|
||||||
.and change { status.account.featured_tags.find_by(name: 'bar').last_status_at }.from(nil).to(be_present)
|
.and change { status.account.featured_tags.find_by(name: 'bar').last_status_at }.from(nil).to(be_present)
|
||||||
|
|||||||
13
spec/support/examples/fasp/api.rb
Normal file
13
spec/support/examples/fasp/api.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_examples 'forbidden for unconfirmed provider' do
|
||||||
|
context 'when the requesting provider is unconfirmed' do
|
||||||
|
let(:provider) { Fabricate(:fasp_provider) }
|
||||||
|
|
||||||
|
it 'returns http unauthorized' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(401)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,10 +8,10 @@ RSpec.describe Fasp::AnnounceAccountLifecycleEventWorker do
|
|||||||
subject { described_class.new.perform(account_uri, 'new') }
|
subject { described_class.new.perform(account_uri, 'new') }
|
||||||
|
|
||||||
let(:account_uri) { 'https://masto.example.com/accounts/1' }
|
let(:account_uri) { 'https://masto.example.com/accounts/1' }
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
let(:subscription) do
|
let(:subscription) do
|
||||||
Fabricate(:fasp_subscription, category: 'account')
|
Fabricate(:fasp_subscription, fasp_provider: provider, category: 'account')
|
||||||
end
|
end
|
||||||
let(:provider) { subscription.fasp_provider }
|
|
||||||
let(:path) { '/data_sharing/v0/announcements' }
|
let(:path) { '/data_sharing/v0/announcements' }
|
||||||
|
|
||||||
let!(:stubbed_request) do
|
let!(:stubbed_request) do
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ RSpec.describe Fasp::AnnounceContentLifecycleEventWorker do
|
|||||||
subject { described_class.new.perform(status_uri, 'new') }
|
subject { described_class.new.perform(status_uri, 'new') }
|
||||||
|
|
||||||
let(:status_uri) { 'https://masto.example.com/status/1' }
|
let(:status_uri) { 'https://masto.example.com/status/1' }
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
let(:subscription) do
|
let(:subscription) do
|
||||||
Fabricate(:fasp_subscription)
|
Fabricate(:fasp_subscription, fasp_provider: provider)
|
||||||
end
|
end
|
||||||
let(:provider) { subscription.fasp_provider }
|
|
||||||
let(:path) { '/data_sharing/v0/announcements' }
|
let(:path) { '/data_sharing/v0/announcements' }
|
||||||
|
|
||||||
let!(:stubbed_request) do
|
let!(:stubbed_request) do
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ RSpec.describe Fasp::AnnounceTrendWorker do
|
|||||||
subject { described_class.new.perform(status.id, 'favourite') }
|
subject { described_class.new.perform(status.id, 'favourite') }
|
||||||
|
|
||||||
let(:status) { Fabricate(:status) }
|
let(:status) { Fabricate(:status) }
|
||||||
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
let(:subscription) do
|
let(:subscription) do
|
||||||
Fabricate(:fasp_subscription,
|
Fabricate(:fasp_subscription,
|
||||||
|
fasp_provider: provider,
|
||||||
category: 'content',
|
category: 'content',
|
||||||
subscription_type: 'trends',
|
subscription_type: 'trends',
|
||||||
threshold_timeframe: 15,
|
threshold_timeframe: 15,
|
||||||
threshold_likes: 2)
|
threshold_likes: 2)
|
||||||
end
|
end
|
||||||
let(:provider) { subscription.fasp_provider }
|
|
||||||
let(:path) { '/data_sharing/v0/announcements' }
|
let(:path) { '/data_sharing/v0/announcements' }
|
||||||
|
|
||||||
let!(:stubbed_request) do
|
let!(:stubbed_request) do
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ RSpec.describe Fasp::BackfillWorker do
|
|||||||
|
|
||||||
subject { described_class.new.perform(backfill_request.id) }
|
subject { described_class.new.perform(backfill_request.id) }
|
||||||
|
|
||||||
let(:backfill_request) { Fabricate(:fasp_backfill_request) }
|
let(:provider) { Fabricate(:confirmed_fasp) }
|
||||||
let(:provider) { backfill_request.fasp_provider }
|
let(:backfill_request) { Fabricate(:fasp_backfill_request, fasp_provider: provider) }
|
||||||
let(:status) { Fabricate(:status) }
|
let(:status) { Fabricate(:status) }
|
||||||
let(:path) { '/data_sharing/v0/announcements' }
|
let(:path) { '/data_sharing/v0/announcements' }
|
||||||
|
|
||||||
|
|||||||
33
spec/workers/purge_custom_emoji_worker_spec.rb
Normal file
33
spec/workers/purge_custom_emoji_worker_spec.rb
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PurgeCustomEmojiWorker do
|
||||||
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
|
let(:domain) { 'evil' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Fabricate(:custom_emoji)
|
||||||
|
Fabricate(:custom_emoji, domain: 'example.com')
|
||||||
|
Fabricate.times(5, :custom_emoji, domain: domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
context 'when domain is nil' do
|
||||||
|
it 'does not delete emojis' do
|
||||||
|
expect { worker.perform(nil) }
|
||||||
|
.to_not(change(CustomEmoji, :count))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when passing a domain' do
|
||||||
|
it 'deletes emojis from this domain only' do
|
||||||
|
expect { worker.perform(domain) }
|
||||||
|
.to change { CustomEmoji.where(domain: domain).count }.to(0)
|
||||||
|
.and not_change { CustomEmoji.local.count }
|
||||||
|
.and(not_change { CustomEmoji.where(domain: 'example.com').count })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -375,6 +375,7 @@ const startServer = async () => {
|
|||||||
req.scopes = result.rows[0].scopes.split(' ');
|
req.scopes = result.rows[0].scopes.split(' ');
|
||||||
req.accountId = result.rows[0].account_id;
|
req.accountId = result.rows[0].account_id;
|
||||||
req.chosenLanguages = result.rows[0].chosen_languages;
|
req.chosenLanguages = result.rows[0].chosen_languages;
|
||||||
|
req.permissions = result.rows[0].permissions;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessTokenId: result.rows[0].id,
|
accessTokenId: result.rows[0].id,
|
||||||
@@ -600,13 +601,13 @@ const startServer = async () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} kind
|
* @param {string} kind
|
||||||
* @param {ResolvedAccount} account
|
* @param {Request} req
|
||||||
* @returns {Promise.<{ localAccess: boolean, remoteAccess: boolean }>}
|
* @returns {Promise.<{ localAccess: boolean, remoteAccess: boolean }>}
|
||||||
*/
|
*/
|
||||||
const getFeedAccessSettings = async (kind, account) => {
|
const getFeedAccessSettings = async (kind, req) => {
|
||||||
const access = { localAccess: true, remoteAccess: true };
|
const access = { localAccess: true, remoteAccess: true };
|
||||||
|
|
||||||
if (account.permissions & PERMISSION_VIEW_FEEDS) {
|
if (req.permissions & PERMISSION_VIEW_FEEDS) {
|
||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user