Change: #591 ホワイトリストのドメイン一覧の保存先・画面変更 (#689)

* Change: #591 ホワイトリストのドメイン一覧の保存先・画面変更

* Update account_batch.rb

* 表示まわりを改善

* Update dangerous.rake
This commit is contained in:
KMY(雪あすか) 2024-04-03 12:09:43 +09:00 committed by GitHub
parent 8c399cefce
commit ff2860d0df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 211 additions and 49 deletions

View file

@ -32,4 +32,5 @@ linters:
- 'app/views/application/_sidebar.html.haml'
- 'app/views/admin/ng_rules/_ng_rule_fields.html.haml'
- 'app/views/admin/ng_words/keywords/_ng_word.html.haml'
- 'app/views/admin/ng_words/white_list/_specified_domain.html.haml'
- 'app/views/admin/sensitive_words/_sensitive_word.html.haml'

View file

@ -2,10 +2,33 @@
module Admin
class NgWords::WhiteListController < NgWordsController
def show
super
@white_list_domains = SpecifiedDomain.white_list_domain_caches.presence || [SpecifiedDomain.new]
end
protected
def validate
begin
SpecifiedDomain.save_from_raws_as_white_list(settings_params_list)
return true
rescue
flash[:alert] = I18n.t('admin.ng_words.save_error')
redirect_to after_update_redirect_path
end
false
end
def after_update_redirect_path
admin_ng_words_white_list_path
end
private
def settings_params_list
params.require(:form_admin_settings)[:specified_domains]
end
end
end

View file

@ -316,40 +316,21 @@ const removeTableRow = (target: EventTarget | null, tableId: string) => {
tableElement.removeChild(tableRowElement);
};
Rails.delegate(
document,
'#sensitive-words-table .add-row-button',
'click',
(ev) => {
const setupTableList = (id: string) => {
Rails.delegate(document, `#${id} .add-row-button`, 'click', (ev) => {
ev.preventDefault();
addTableRow('sensitive-words-table');
},
);
addTableRow(id);
});
Rails.delegate(
document,
'#sensitive-words-table .delete-row-button',
'click',
(ev) => {
Rails.delegate(document, `#${id} .delete-row-button`, 'click', (ev) => {
ev.preventDefault();
removeTableRow(ev.target, 'sensitive-words-table');
},
);
removeTableRow(ev.target, id);
});
};
Rails.delegate(document, '#ng-words-table .add-row-button', 'click', (ev) => {
ev.preventDefault();
addTableRow('ng-words-table');
});
Rails.delegate(
document,
'#ng-words-table .delete-row-button',
'click',
(ev) => {
ev.preventDefault();
removeTableRow(ev.target, 'ng-words-table');
},
);
setupTableList('sensitive-words-table');
setupTableList('ng-words-table');
setupTableList('white-list-table');
async function mountReactComponent(element: Element) {
const componentName = element.getAttribute('data-admin-component');

View file

@ -98,10 +98,10 @@ class Form::AccountBatch
def approve_remote_domain!
domains = accounts.group_by(&:domain).pluck(0)
if (Setting.permit_new_account_domains || []).compact_blank.present?
list = ((Setting.permit_new_account_domains || []) + domains).compact_blank.uniq.join("\n")
Form::AdminSettings.new(permit_new_account_domains: list).save
(domains - SpecifiedDomain.where(domain: domains, table: 0).pluck(:domain)).each do |domain|
SpecifiedDomain.create!(domain: domain, table: 0)
end
Account.where(domain: domains, remote_pending: true).find_each do |account|
approve_remote_account(account)
end

View file

@ -61,7 +61,6 @@ class Form::AdminSettings
unlocked_friend
enable_local_timeline
emoji_reaction_disallow_domains
permit_new_account_domains
block_unfollow_account_mention
hold_remote_new_accounts
).freeze
@ -121,7 +120,6 @@ class Form::AdminSettings
STRING_ARRAY_KEYS = %i(
emoji_reaction_disallow_domains
permit_new_account_domains
).freeze
attr_accessor(*KEYS)

View file

@ -0,0 +1,78 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: specified_domains
#
# id :bigint(8) not null, primary key
# domain :string not null
# table :integer default(0), not null
# options :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
#
class SpecifiedDomain < ApplicationRecord
attr_accessor :domains
validates :domain, uniqueness: { scope: :table }
after_commit :invalidate_cache!
scope :white_list_domains, -> { where(table: 0) }
class << self
def white_list_domain_caches
Rails.cache.fetch('specified_domains:white_list') { white_list_domains.to_a }
end
def save_from_hashes(rows, type, caches)
unmatched = caches
matched = []
SpecifiedDomain.transaction do
rows.filter { |item| item[:domain].present? }.each do |item|
exists = unmatched.find { |i| i.domain == item[:domain] }
if exists.present?
unmatched.delete(exists)
matched << exists
next unless item.key?(:options) && item[:options] == exists.options
exists.update!(options: item[:options])
elsif matched.none? { |i| i.domain == item[:domain] }
SpecifiedDomain.create!(
domain: item[:domain],
table: type,
options: item[:options] || {}
)
end
end
SpecifiedDomain.destroy(unmatched.map(&:id))
end
true
end
def save_from_raws(rows, type, caches)
hashes = (rows['domains'] || []).map do |domain|
{
domain: domain,
type: type,
}
end
save_from_hashes(hashes, type, caches)
end
def save_from_raws_as_white_list(rows)
save_from_raws(rows, 0, white_list_domain_caches)
end
end
private
def invalidate_cache!
Rails.cache.delete('specified_domains:white_list')
end
end

View file

@ -142,11 +142,7 @@ class ActivityPub::ProcessAccountService < BaseService
def blocking_new_account?
return false unless Setting.hold_remote_new_accounts
permit_new_account_domains.exclude?(@domain)
end
def permit_new_account_domains
(Setting.permit_new_account_domains || []).compact_blank
SpecifiedDomain.white_list_domain_caches.none? { |item| item.domain == @domain }
end
def valid_account?

View file

@ -0,0 +1,6 @@
- temporary_id = defined?(@temp_id) ? @temp_id += 1 : @temp_id = 1
%tr{ class: template ? 'template-row' : nil }
%td= f.input :domains, as: :string, input_html: { multiple: true, value: specified_domain.domain }
%td
= hidden_field_tag :'form_admin_settings[specified_domains][temporary_ids][]', temporary_id, class: 'temporary_id'
= link_to safe_join([fa_icon('times'), t('filters.index.delete')]), '#', class: 'table-action-link delete-row-button'

View file

@ -18,8 +18,24 @@
.fields-group
= f.input :hold_remote_new_accounts, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hold_remote_new_accounts'), hint: t('admin.ng_words.remote_approval_hint')
.fields-group
= f.input :permit_new_account_domains, wrapper: :with_label, as: :text, kmyblue: true, input_html: { rows: 6 }, label: t('admin.ng_words.permit_new_account_domains')
%h4= t('admin.ng_words.white_list_header')
.table-wrapper
%table.table.keywords-table#white-list-table
%thead
%tr
%th= t('simple_form.labels.defaults.domain')
%th
%tbody
= f.simple_fields_for :specified_domains, @white_list_domains do |domain|
= render partial: 'specified_domain', collection: @white_list_domains, locals: { f: domain, template: false }
= f.simple_fields_for :specified_domains, @white_list_domains do |domain|
= render partial: 'specified_domain', collection: [SpecifiedDomain.new], locals: { f: domain, template: true }
%tfoot
%tr
%td{ colspan: 2 }
= link_to safe_join([fa_icon('plus'), t('admin.ng_words.edit.add_domain')]), '#', class: 'table-action-link add-row-button'
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -749,11 +749,12 @@ en:
block_unfollow_account_mention_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。NGルールで代替してください。
deprecated: Will remove settings
deprecated_hint: These settings will be removed in the next LTS or kmyblue version 14.0, whichever comes first. Please refer to the description of each setting and replace them with the new settings if possible.
edit:
add_domain: Add domain
hide_local_users_for_anonymous: Hide timeline local user posts from anonymous
hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。
hold_remote_new_accounts: Hold new remote accounts
keywords: Reject keywords
permit_new_account_domains: Domain list to automatically approve new users
preamble: This setting is useful for solving problems related to spam that are difficult to address with domain blocking. You can reject posts that meet certain criteria, such as the inclusion of specific keywords. Please consider your settings carefully and check your history regularly to ensure that problem-free submissions are not deleted.
post_hash_tags_max: Hash tags max for posts
post_mentions_max: Mentions max for posts
@ -771,6 +772,7 @@ en:
test_error: Testing is returned any errors
title: NG words and against spams
white_list: White list
white_list_header: List of domains for immediate approval of remote accounts
white_list_hint: Whitelisting can be used as a last resort when exposed to severe attacks. External attacks do not disappear immediately, but they can be reduced gradually and reliably through moderation. In addition, a regular remote account approval process is required.
ngword_histories:
back_to_ng_words: NG words and against spams

View file

@ -748,11 +748,12 @@ ja:
block_unfollow_account_mention_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。NGルールで代替してください。
deprecated: 新しいバージョンで削除する予定の設定
deprecated_hint: これらの設定は、次回のLTS、またはkmyblueバージョン14.0のどちらか早い方で削除する予定です。それぞれの設定の説明を参照して、可能であれば新しい設定に置き換えてください。
edit:
add_domain: ドメインを追加
hide_local_users_for_anonymous: ログインしていない状態でローカルユーザーの投稿をタイムラインから取得できないようにする
hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。
hold_remote_new_accounts: リモートの新規アカウントを保留する
keywords: 拒否するキーワード
permit_new_account_domains: 新規ユーザーを自動承認するドメイン
phrases:
regexp_html: <strong>正規</strong> 表現 にチェックの入っている項目は、正規表現を用いての比較となります。
regexp_short: 正規
@ -770,6 +771,7 @@ ja:
test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません
title: NGワードとスパム
white_list: ホワイトリスト
white_list_header: リモートアカウントを即座に承認するドメイン一覧
white_list_hint: 激しい攻撃に晒された場合の最終手段として、ホワイトリストが利用できます。外部からの攻撃が即座に消えるわけではありませんが、モデレーションを進めることで徐々に確実に減らすことができます。また、定期的なリモートアカウント承認作業が求められます。
ngword_histories:
back_to_ng_words: NGワードとスパム

View file

@ -218,6 +218,7 @@ en:
discoverable: Suggest account to others
discoverable_local: Disallow suggesting account on other servers
display_name: Display name
domain: Domain
email: E-mail address
expires_in: Expire after
fields: Extra fields

View file

@ -229,6 +229,7 @@ ja:
discoverable: ディレクトリに掲載する
discoverable_local: 他サーバーのディレクトリに掲載しない
display_name: 表示名
domain: ドメイン
email: メールアドレス
expires_in: 有効期限
fields: プロフィール補足情報

View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
class CreateSpecifiedDomains < ActiveRecord::Migration[7.1]
class Setting < ApplicationRecord
def value
YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess, Symbol]) if self[:value].present?
end
def value=(new_value)
self[:value] = new_value.to_yaml
end
end
class SpecifiedDomain < ApplicationRecord; end
def up
create_table :specified_domains do |t|
t.string :domain, null: false
t.integer :table, default: 0, null: false
t.jsonb :options, null: false, default: {}
t.timestamps
end
add_index :specified_domains, %i(domain table), unique: true
setting = Setting.find_by(var: :permit_new_account_domains)
(setting&.value || []).compact.uniq.each do |domain|
SpecifiedDomain.create!(domain: domain, table: 0)
end
setting&.destroy
end
def down
Setting.find_by(var: :permit_new_account_domains)&.destroy
Setting.new(var: :permit_new_account_domains).tap { |s| s.value = SpecifiedDomain.where(table: 0).pluck(:domain) }.save!
drop_table :specified_domains
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_03_27_234026) do
ActiveRecord::Schema[7.1].define(version: 2024_04_01_222541) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -1328,6 +1328,15 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_27_234026) do
t.index ["version"], name: "index_software_updates_on_version", unique: true
end
create_table "specified_domains", force: :cascade do |t|
t.string "domain", null: false
t.integer "table", default: 0, null: false
t.jsonb "options", default: {}, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["domain", "table"], name: "index_specified_domains_on_domain_and_table", unique: true
end
create_table "status_capability_tokens", force: :cascade do |t|
t.bigint "status_id", null: false
t.string "token"

View file

@ -97,6 +97,7 @@ namespace :dangerous do
20240320231633
20240326231854
20240327234026
20240401222541
)
# Removed: account_groups
target_tables = %w(
@ -121,6 +122,7 @@ namespace :dangerous do
pending_statuses
scheduled_expiration_statuses
sensitive_words
specified_domains
status_capability_tokens
status_references
)

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
Fabricator(:specified_domain) do
domain { sequence(:domain) { |i| "example_#{i}.com" } }
end

View file

@ -13,7 +13,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
subject { described_class.new.call('alice', 'example.com', payload) }
let(:hold_remote_new_accounts) { true }
let(:permit_new_account_domains) { nil }
let(:permit_domain) { nil }
let(:payload) do
{
id: 'https://foo.test',
@ -26,7 +26,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
before do
Setting.hold_remote_new_accounts = hold_remote_new_accounts
Setting.permit_new_account_domains = permit_new_account_domains
Fabricate(:specified_domain, domain: permit_domain, table: 0) if permit_domain
end
it 'creates pending account in a simple case' do
@ -37,7 +37,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
end
context 'when is blocked' do
let(:permit_new_account_domains) { ['foo.bar'] }
let(:permit_domain) { 'foo.bar' }
it 'creates pending account' do
expect(subject).to_not be_nil
@ -98,7 +98,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
end
context 'when is in whitelist' do
let(:permit_new_account_domains) { ['example.com'] }
let(:permit_domain) { 'example.com' }
it 'does not create account' do
expect(subject).to_not be_nil