Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: ホワイトリスト運用時、承認待ちリモートアカウントの概念ならびに操作画面 #584

Merged
merged 3 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/controllers/admin/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ def action_from_button
'approve'
elsif params[:reject]
'reject'
elsif params[:approve_remote]
'approve_remote'
elsif params[:approve_remote_domain]
'approve_remote_domain'
elsif params[:reject_remote]
'reject_remote'
end
end
end
Expand Down
1 change: 1 addition & 0 deletions app/helpers/admin/accounts_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def admin_accounts_moderation_options
[t('admin.accounts.moderation.silenced'), 'silenced'],
[t('admin.accounts.moderation.disabled'), 'disabled'],
[t('admin.accounts.moderation.suspended'), 'suspended'],
[t('admin.accounts.moderation.remote_pending'), 'remote_pending'],
[safe_join([t('admin.accounts.moderation.pending'), "(#{pending_user_count_label})"], ' '), 'pending'],
]
end
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/mastodon/components/visibility_icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const messages = defineMessages({
},
circle_short: {
id: 'privacy.circle.short',
defaultMessage: 'Circle members only',
defaultMessage: 'Circle',
},
reply_short: {
id: 'privacy.reply.short',
Expand Down
12 changes: 12 additions & 0 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
# settings :jsonb
# indexable :boolean default(FALSE), not null
# master_settings :jsonb
# remote_pending :boolean default(FALSE), not null
#

class Account < ApplicationRecord
Expand Down Expand Up @@ -122,6 +123,7 @@ class Account < ApplicationRecord
scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
scope :silenced, -> { where.not(silenced_at: nil) }
scope :suspended, -> { where.not(suspended_at: nil) }
scope :remote_pending, -> { where(remote_pending: true).where.not(suspended_at: nil) }
scope :sensitized, -> { where.not(sensitized_at: nil) }
scope :without_suspended, -> { where(suspended_at: nil) }
scope :without_silenced, -> { where(silenced_at: nil) }
Expand Down Expand Up @@ -295,6 +297,16 @@ def unsuspend!
end
end

def approve_remote!
update!(remote_pending: false)
unsuspend!
end

def reject_remote!
update!(remote_pending: false)
suspend!
end

def sensitized?
sensitized_at.present?
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/account_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ def status_scope(value)
Account.without_suspended
when 'pending'
accounts_with_users.merge(User.pending)
when 'remote_pending'
Account.remote_pending
when 'suspended'
Account.suspended
when 'disabled'
Expand Down
46 changes: 46 additions & 0 deletions app/models/form/account_batch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ def save
approve!
when 'reject'
reject!
when 'approve_remote'
approve_remote!
when 'approve_remote_domain'
approve_remote_domain!
when 'reject_remote'
reject_remote!
when 'suppress_follow_recommendation'
suppress_follow_recommendation!
when 'unsuppress_follow_recommendation'
Expand Down Expand Up @@ -84,6 +90,29 @@ def reject!
end
end

def approve_remote!
accounts.find_each do |account|
approve_remote_account(account)
end
end

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
end
Account.where(domain: domains, remote_pending: true).find_each do |account|
approve_remote_account(account)
end
end

def reject_remote!
accounts.find_each do |account|
reject_remote_account(account)
end
end

def suspend!
accounts.find_each do |account|
if account.user_pending?
Expand Down Expand Up @@ -115,10 +144,21 @@ def reject_account(account)
AccountDeletionWorker.perform_async(account.id, { 'reserve_username' => false })
end

def reject_remote_account(account)
authorize(account, :reject_remote?)
log_action(:reject_remote, account)
account.reject_remote!
process_suspend(account)
end

def suspend_account(account)
authorize(account, :suspend?)
log_action(:suspend, account)
account.suspend!(origin: :local)
process_suspend(account)
end

def process_suspend(account)
account.strikes.create!(
account: current_account,
action: :suspend
Expand All @@ -143,6 +183,12 @@ def approve_account(account)
account.user.approve!
end

def approve_remote_account(account)
authorize(account, :approve_remote?)
log_action(:approve_remote, account)
account.approve_remote!
end

def select_all_matching?
select_all_matching == '1'
end
Expand Down
8 changes: 8 additions & 0 deletions app/policies/account_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ def unblock_email?
def review?
role.can?(:manage_taxonomies)
end

def approve_remote?
role.can?(:manage_users) && record.remote_pending
end

def reject_remote?
role.can?(:manage_users) && record.remote_pending
end
end
25 changes: 16 additions & 9 deletions app/services/activitypub/process_account_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ def call(username, domain, json, options = {}) # rubocop:disable Metrics/Perceiv
@suspension_changed = false

if @account.nil?
return nil if blocking_new_account?(@domain)

with_redis do |redis|
return nil if redis.pfcount("unique_subdomains_for:#{PublicSuffix.domain(@domain, ignore_private: true)}") >= SUBDOMAINS_RATELIMIT

Expand All @@ -61,7 +59,7 @@ def call(username, domain, json, options = {}) # rubocop:disable Metrics/Perceiv
clear_tombstones! if key_changed?
after_suspension_change! if suspension_changed?

unless @options[:only_key] || @account.suspended?
unless @options[:only_key] || (@account.suspended? && [email protected]_pending)
check_featured_collection! if @account.featured_collection_url.present?
check_featured_tags_collection! if @json['featuredTags'].present?
check_links! if @account.fields.any?(&:requires_verification?)
Expand All @@ -87,6 +85,12 @@ def create_account
@account.silenced_at = domain_block.created_at if auto_silence?
@account.searchability = :direct # not null

if @account.suspended_at.nil? && blocking_new_account?
@account.suspended_at = Time.now.utc
@account.suspension_origin = :local
@account.remote_pending = true
end

set_immediate_protocol_attributes!

@account.save!
Expand All @@ -98,9 +102,12 @@ def update_account

set_suspension!
set_immediate_protocol_attributes!
set_fetchable_key! unless @account.suspended? && @account.suspension_origin_local?
set_immediate_attributes! unless @account.suspended?
set_fetchable_attributes! unless @options[:only_key] || @account.suspended?

freeze_data = @account.suspended? && (@account.suspension_origin_remote? || [email protected]_pending)

set_fetchable_key! unless @account.suspended? && @account.suspension_origin_local? && [email protected]_pending
set_immediate_attributes! unless freeze_data
set_fetchable_attributes! unless @options[:only_key] || freeze_data

@account.save_with_optional_media!
end
Expand Down Expand Up @@ -132,10 +139,10 @@ def set_immediate_attributes!
@account.memorial = @json['memorial'] || false
end

def blocking_new_account?(domain)
def blocking_new_account?
return false if permit_new_account_domains.blank?

permit_new_account_domains.exclude?(domain)
permit_new_account_domains.exclude?(@domain)
end

def permit_new_account_domains
Expand Down Expand Up @@ -410,7 +417,7 @@ def moved_account
end

def skip_download?
@account.suspended? || domain_block&.reject_media?
(@account.suspended? && [email protected]_pending) || domain_block&.reject_media?
end

def auto_suspend?
Expand Down
7 changes: 7 additions & 0 deletions app/views/admin/accounts/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@

= f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }

- elsif @accounts.any?(&:remote_pending)
= f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve_remote, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }

= f.button safe_join([fa_icon('check'), t('admin.accounts.approve_domain')]), name: :approve_remote_domain, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }

= f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject_remote, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }

= f.button safe_join([fa_icon('lock'), t('admin.accounts.perform_full_suspension')]), name: :suspend, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
- if @accounts.total_count > @accounts.size
.batch-table__select-all
Expand Down
6 changes: 5 additions & 1 deletion app/views/admin/ng_words/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
.fields-group
= f.input :hide_local_users_for_anonymous, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hide_local_users_for_anonymous')

%p.hint
= t 'admin.ng_words.remote_approval_hint'
= link_to t('admin.ng_words.remote_approval_list'), admin_accounts_path(status: 'remote_pending', origin: 'remote')

.fields-group
= f.input :permit_new_account_domains, wrapper: :with_label, as: :text, kmyblue: true, input_html: { rows: 6 }, label: t('admin.special_instances.permit_new_account_domains'), hint: t('admin.special_instances.permit_new_account_domains_hint')
= 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')

.actions
= f.button :button, t('generic.save_changes'), type: :submit
2 changes: 1 addition & 1 deletion app/workers/redownload_avatar_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class RedownloadAvatarWorker
def perform(id)
account = Account.find(id)

return if account.suspended? || DomainBlock.rule_for(account.domain)&.reject_media?
return if (account.suspended? && !account.remote_pending) || DomainBlock.rule_for(account.domain)&.reject_media?
return if account.avatar_remote_url.blank? || account.avatar_file_name.present?

account.reset_avatar!
Expand Down
2 changes: 1 addition & 1 deletion app/workers/redownload_header_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class RedownloadHeaderWorker
def perform(id)
account = Account.find(id)

return if account.suspended? || DomainBlock.rule_for(account.domain)&.reject_media?
return if (account.suspended? && !account.remote_pending) || DomainBlock.rule_for(account.domain)&.reject_media?
return if account.header_remote_url.blank? || account.header_file_name.present?

account.reset_header!
Expand Down
9 changes: 7 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ en:
accounts:
add_email_domain_block: Block e-mail domain
approve: Approve
approve_domain: Approve domain
approved_msg: Successfully approved %{username}'s sign-up application
are_you_sure: Are you sure?
avatar: Avatar
Expand Down Expand Up @@ -93,6 +94,7 @@ en:
all: All
disabled: Disabled
pending: Pending
remote_pending: Pending (Remote)
silenced: Limited
suspended: Suspended
title: Moderation
Expand Down Expand Up @@ -230,6 +232,7 @@ en:
update_user_role: Update Role
actions:
approve_appeal_html: "%{name} approved moderation decision appeal from %{target}"
approve_remote_account_html: "%{name} approved %{target} join on this server"
approve_user_html: "%{name} approved sign-up from %{target}"
assigned_to_self_report_html: "%{name} assigned report %{target} to themselves"
change_email_user_html: "%{name} changed the e-mail address of user %{target}"
Expand Down Expand Up @@ -269,6 +272,7 @@ en:
memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page"
promote_user_html: "%{name} promoted user %{target}"
reject_appeal_html: "%{name} rejected moderation decision appeal from %{target}"
reject_remote_account_html: "%{name} rejected %{target} join on this server"
reject_user_html: "%{name} rejected sign-up from %{target}"
remove_avatar_user_html: "%{name} removed %{target}'s avatar"
remove_history_status_html: "%{name} removed post edit histories by %{target}"
Expand Down Expand Up @@ -647,9 +651,12 @@ en:
keywords_for_stranger_mention: Reject keywords when mention/reply/reference/quote from strangers
keywords_for_stranger_mention_hint: This words are checked posts from other servers only.
keywords_hint: The first character of the line is "?". to use regular expressions
permit_new_account_domains: Domain list to automatically approve new users
post_hash_tags_max: Hash tags max for posts
post_mentions_max: Mentions max for posts
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (If the mentions include at least one person who is not a follower of yours)
remote_approval_list: List of remote accounts awaiting approval
remote_approval_hint: If you set one or more domains on the list of domains for which you want to automatically approve new users, newly recognized accounts on unspecified domains will be placed in suspend status. You can review that list and approve them if necessary. If none is specified, all remote accounts are approved immediately.
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります
test_error: Testing is returned any errors
Expand Down Expand Up @@ -918,8 +925,6 @@ en:
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.
permit_new_account_domains: Domain to allow recognition of new accounts
permit_new_account_domains_hint: Only new account information sent from the domain specified here will be saved if more than one is specified,
title: Special servers
statuses:
account: Author
Expand Down
9 changes: 7 additions & 2 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ja:
accounts:
add_email_domain_block: メールドメインブロックに追加
approve: 承認
approve_domain: ドメインを承認
approved_msg: '%{username}さんの登録申請を承認しました'
are_you_sure: 本当に実行しますか?
avatar: アイコン
Expand Down Expand Up @@ -91,6 +92,7 @@ ja:
all: すべて
disabled: 無効化済み
pending: 承認待ち
remote_pending: 承認待ち (リモート)
silenced: 制限
suspended: 停止済み
title: モデレーション
Expand Down Expand Up @@ -227,6 +229,7 @@ ja:
update_user_role: ロールを更新
actions:
approve_appeal_html: "%{name}さんが%{target}さんからの抗議を承認しました"
approve_remote_account_html: "%{name}さんが%{target}さんの参加を承認しました"
approve_user_html: "%{name}さんが%{target}さんからの登録を承認しました"
assigned_to_self_report_html: "%{name}さんが通報 %{target}を自身の担当に割り当てました"
change_email_user_html: "%{name}さんが%{target}さんのメールアドレスを変更しました"
Expand Down Expand Up @@ -266,6 +269,7 @@ ja:
memorialize_account_html: "%{name}さんが%{target}さんを追悼アカウントページに登録しました"
promote_user_html: "%{name}さんが%{target}さんを昇格しました"
reject_appeal_html: "%{name}さんが%{target}からの抗議を却下しました"
reject_remote_account_html: "%{name}さんが%{target}さんの参加を却下しました"
reject_user_html: "%{name}さんが%{target}さんからの登録を拒否しました"
remove_avatar_user_html: "%{name}さんが%{target}さんのアイコンを削除しました"
remove_history_status_html: "%{name}さんが%{target}さんの投稿の編集履歴を削除しました"
Expand Down Expand Up @@ -640,9 +644,12 @@ ja:
keywords_for_stranger_mention: フォローしていないアカウントへのメンションや参照で利用できないキーワード
keywords_for_stranger_mention_hint: フォローしていないアカウントへのメンション、参照、引用にのみ適用されます
keywords_hint: 行を「?」で始めると、正規表現が使えます
permit_new_account_domains: 新規ユーザーを自動承認するドメイン
post_hash_tags_max: 投稿に設定可能なハッシュタグの最大数
post_mentions_max: 投稿に設定可能なメンションの最大数
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (メンション先にフォロワー以外を1人でも含む場合)
remote_approval_list: 承認待ちのリモートアカウント一覧
remote_approval_hint: 新規ユーザーを自動承認するドメインリストに1つ以上のドメインを設定すると、指定されていないドメインで新しく認識されたアカウントはサスペンド状態になります。その一覧を確認し、必要であれば承認を行うことができます。何も指定しなかった場合、全てのリモートアカウントが即座に承認されます。
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります
test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません
Expand Down Expand Up @@ -909,8 +916,6 @@ ja:
special_instances:
emoji_reaction_disallow_domains: 自分のサーバーが絵文字リアクションをすることを許可しないドメイン
emoji_reaction_disallow_domains_hint: 連合先に配慮する必要がある場合、ドメインを改行区切りで設定します。設定されたドメインの投稿に絵文字リアクションを付けることはできません。
permit_new_account_domains: 新規アカウントの認知を許可するドメイン
permit_new_account_domains_hint: 1つ以上指定した場合、ここで指定されたドメインから送られてくる新規アカウント情報だけが保存されるようになります
title: 特殊なサーバー
statuses:
account: 作成者
Expand Down
11 changes: 11 additions & 0 deletions db/migrate/20240217093511_add_remote_pending_to_accounts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class AddRemotePendingToAccounts < ActiveRecord::Migration[7.1]
disable_ddl_transaction!

def change
add_column :accounts, :remote_pending, :boolean, null: false, default: false

add_index :accounts, :remote_pending, unique: false, algorithm: :concurrently
end
end
Loading
Loading