diff --git a/app/controllers/admin/special_domains_controller.rb b/app/controllers/admin/special_domains_controller.rb new file mode 100644 index 0000000000..0ddbf26786 --- /dev/null +++ b/app/controllers/admin/special_domains_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Admin + class SpecialDomainsController < BaseController + def show + authorize :instance, :show? + + @admin_settings = Form::AdminSettings.new + end + + def create + authorize :instance, :destroy? + + @admin_settings = Form::AdminSettings.new(settings_params) + + if @admin_settings.save + flash[:notice] = I18n.t('generic.changes_saved_msg') + redirect_to after_update_redirect_path + else + render :show + end + end + + private + + def after_update_redirect_path + admin_special_domains_path + end + + def settings_params + params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS) + end + end +end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 171bac8975..c47bdc71e3 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -63,6 +63,8 @@ class Form::AdminSettings emoji_reaction_disallow_domains block_unfollow_account_mention hold_remote_new_accounts + stop_fetch_activity_domains + stop_link_preview_domains ).freeze INTEGER_KEYS = %i( @@ -120,6 +122,8 @@ class Form::AdminSettings STRING_ARRAY_KEYS = %i( emoji_reaction_disallow_domains + stop_fetch_activity_domains + stop_link_preview_domains ).freeze attr_accessor(*KEYS) diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 6c84f6d3b4..30e4f23abb 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -106,7 +106,8 @@ class FetchLinkCardService < BaseService def bad_url?(uri) # Avoid local instance URLs and invalid URLs - uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) + uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) || + referenced_urls.include?(uri.to_s) || Setting.stop_link_preview_domains&.include?(uri.host) end def mention_link?(anchor) diff --git a/app/services/process_references_service.rb b/app/services/process_references_service.rb index 807e1571b2..bbb22a0575 100644 --- a/app/services/process_references_service.rb +++ b/app/services/process_references_service.rb @@ -153,10 +153,16 @@ class ProcessReferencesService < BaseService def url_to_status(url) status = ActivityPub::TagManager.instance.uri_to_resource(url, Status, url: true) - status ||= ResolveURLService.new.call(url, on_behalf_of: @status.account) if @fetch_remote && @no_fetch_urls.exclude?(url) + status ||= ResolveURLService.new.call(url, on_behalf_of: @status.account) unless bad_url_to_fetch?(url) referrable?(status) ? status : nil end + def bad_url_to_fetch?(url) + uri = Addressable::URI.parse(url).normalize + + !@fetch_remote || @no_fetch_urls.include?(url) || uri.host.blank? || Setting.stop_fetch_activity_domains&.include?(uri.host) + end + def referrable?(target_status) return false if target_status.nil? return @referrable if defined?(@referrable) diff --git a/app/views/admin/special_domains/show.html.haml b/app/views/admin/special_domains/show.html.haml new file mode 100644 index 0000000000..7a94c2cf2c --- /dev/null +++ b/app/views/admin/special_domains/show.html.haml @@ -0,0 +1,17 @@ +- content_for :page_title do + = t('admin.special_domains.title') + +- content_for :header_tags do + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' + += simple_form_for @admin_settings, url: admin_special_domains_path, html: { method: :post } do |f| + = render 'shared/error_messages', object: @admin_settings + + .fields-group + = f.input :stop_fetch_activity_domains, wrapper: :with_label, as: :text, input_html: { rows: 6 }, label: t('admin.special_domains.stop_fetch_activity_domains.title'), hint: t('admin.special_domains.stop_fetch_activity_domains.preamble') + + .fields-group + = f.input :stop_link_preview_domains, wrapper: :with_label, as: :text, input_html: { rows: 6 }, label: t('admin.special_domains.stop_link_preview_domains.title'), hint: t('admin.special_domains.stop_link_preview_domains.preamble') + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/en.yml b/config/locales/en.yml index a96b9e3353..4eacd39e6a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1046,6 +1046,14 @@ en: minor: Minor release patch: Patch release — bugfixes and easy to apply changes version: Version + special_domains: + stop_fetch_activity_domains: + preamble: WebとActivityでドメインの異なるサーバーでは、WebにアクセスしてもActivityが取得できない場合があります。参照において、投稿本文内でそのようなURLが指定された場合のフェッチを抑止します。多くの場合、この設定を利用する機会はほとんどありません。 + title: Domains do not fetch for references + stop_link_preview_domains: + preamble: 多くのアクセスがあったIPアドレスからの接続を一時的に制限するサイトがある場合、自分のサーバーが不利に扱われる場合があります。そのようなサイトにアクセスしてリンクプレビューを生成する処理を抑制します。多くの場合、この設定を利用する機会はほとんどありません。 + title: Domains do not fetch for link previews + title: Special domains special_instances: emoji_reaction_disallow_domains: Domains we are not permitted emoji reaction emoji_reaction_disallow_domains_hint: If you need to be considerate to your coalition partners, set the domain with a new line separator. It is not possible to put an emoji reaction on a post from a set domain. diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 9c7ad5ebf1..5bce8c7eb9 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1042,6 +1042,14 @@ ja: minor: マイナーリリース patch: パッチ (バグ修正のみ) version: バージョン + special_domains: + stop_fetch_activity_domains: + preamble: WebとActivityでドメインの異なるサーバーでは、WebにアクセスしてもActivityが取得できない場合があります。参照において、投稿本文内でそのようなURLが指定された場合のフェッチを抑止します。ドメインは改行区切りで複数指定できます。多くの場合、この設定を利用する機会はほとんどありません。 + title: 参照でフェッチを行わないドメイン + stop_link_preview_domains: + preamble: 多くのアクセスがあったIPアドレスからの接続を一時的に制限するサイトがある場合、自分のサーバーが不利に扱われる場合があります。そのようなサイトにアクセスしてリンクプレビューを生成する処理を抑制します。ドメインは改行区切りで複数指定できます。多くの場合、この設定を利用する機会はほとんどありません。 + title: リンクのプレビューを行わないドメイン + title: 特殊なドメイン special_instances: emoji_reaction_disallow_domains: 自分のサーバーが絵文字リアクションをすることを許可しないドメイン emoji_reaction_disallow_domains_hint: 連合先に配慮する必要がある場合、ドメインを改行区切りで設定します。設定されたドメインの投稿に絵文字リアクションを付けることはできません。 diff --git a/config/navigation.rb b/config/navigation.rb index ab16e6bbec..1a9b14c450 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -60,6 +60,7 @@ SimpleNavigation::Configuration.run do |navigation| s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations}, if: -> { current_user.can?(:manage_taxonomies) } s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_path(limited: limited_federation_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.can?(:manage_federation) } s.item :special_instances, safe_join([fa_icon('list fw'), t('admin.special_instances.title')]), admin_special_instances_path, highlights_on: %r{/admin/special_instances}, if: -> { current_user.can?(:manage_federation) } + s.item :special_domains, safe_join([fa_icon('list fw'), t('admin.special_domains.title')]), admin_special_domains_path, highlights_on: %r{/admin/special_domains}, if: -> { current_user.can?(:manage_federation) } s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_path, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.can?(:manage_blocks) } s.item :ip_blocks, safe_join([fa_icon('ban fw'), t('admin.ip_blocks.title')]), admin_ip_blocks_path, highlights_on: %r{/admin/ip_blocks}, if: -> { current_user.can?(:manage_blocks) } s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_path, if: -> { current_user.can?(:view_audit_log) } diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 6622ffcafc..9fe57b2967 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -46,6 +46,7 @@ namespace :admin do resources :ng_rule_histories, only: [:show] resource :sensitive_words, only: [:show, :create] resource :special_instances, only: [:show, :create] + resource :special_domains, only: [:show, :create] resources :announcements, except: [:show] do member do diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 28559dc708..5f41e2b48b 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -284,6 +284,20 @@ RSpec.describe FetchLinkCardService do expect(status.preview_card.title).to eq 'Hello world' end end + + context 'when URL domain is blocked by admin' do + let(:status) { Fabricate(:status, text: 'http://example.com/html') } + let(:custom_before) { true } + + before do + Setting.stop_link_preview_domains = ['example.com'] + end + + it 'creates preview card' do + subject.call(status) + expect(status.preview_card).to be_nil + end + end end context 'with a remote status' do diff --git a/spec/services/process_references_service_spec.rb b/spec/services/process_references_service_spec.rb index 1ec81a42fb..d54d5e6bd9 100644 --- a/spec/services/process_references_service_spec.rb +++ b/spec/services/process_references_service_spec.rb @@ -175,6 +175,33 @@ RSpec.describe ProcessReferencesService, type: :service do end end + context 'with fetch is blocked by admin' do + let(:text) { 'Hello RT https://web.example.com/note' } + + before do + Setting.stop_fetch_activity_domains = ['web.example.com'] + stub_request(:get, 'https://web.example.com/note').to_return(status: 404) + end + + context 'when the post is known' do + let(:target_status) { Fabricate(:status, uri: 'https://example.com/note', url: 'https://web.example.com/note') } + + it 'post status', :sidekiq_inline do + expect(subject.size).to eq 1 + expect(subject.pluck(0)).to include target_status.id + expect(subject.pluck(1)).to include 'RT' + expect(notify?).to be true + end + end + + context 'when the post is unknown' do + it 'post status', :sidekiq_inline do + expect(subject.size).to eq 0 + expect(a_request(:get, 'https://web.example.com/note')).to_not have_been_made + end + end + end + context 'when unfetched remote post' do let(:account) { Fabricate(:account, followers_url: 'http://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor') } let(:object_json) do