diff options
author | multiple creatures <dev@multiple-creature.party> | 2020-02-17 02:26:52 -0600 |
---|---|---|
committer | multiple creatures <dev@multiple-creature.party> | 2020-02-17 02:26:52 -0600 |
commit | 2427cced78580da729a0ac6a1dc52b2d206aa11c (patch) | |
tree | e0b703674d3a1fb523b447eb512ff0b2ac6ddd65 | |
parent | 8bf7e00362b4e5bf29e3841bd871590871b5257d (diff) |
add a `manual_only` (manual trust only) moderation option + handle more `reject_unknown`/graylist mode caveats
29 files changed, 190 insertions, 40 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index f74082562..25cb2fb72 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,7 +2,7 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :mark_known, :mark_unknown, :allow_public, :allow_nonsensitive, :unsilence, :unsuspend, :memorialize, :approve, :reject, :sync] + before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :mark_known, :mark_unknown, :manual_only, :auto_trust, :allow_public, :allow_nonsensitive, :unsilence, :unsuspend, :memorialize, :approve, :reject, :sync] before_action :require_remote_account!, only: [:redownload, :sync] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] @@ -59,6 +59,20 @@ module Admin redirect_to admin_account_path(@account.id) end + def manual_only + authorize @account, :manual_only? + @account.manual_only! + log_action :manual_only, @account + redirect_to admin_account_path(@account.id) + end + + def auto_trust + authorize @account, :auto_trust? + @account.auto_trust! + log_action :auto_trust, @account + redirect_to admin_account_path(@account.id) + end + def force_sensitive authorize @account, :force_sensitive? @account.force_sensitive! diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index bba47082d..ec368470f 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -63,7 +63,7 @@ module Admin end def resource_params - params.require(:domain_block).permit(:domain, :severity, :force_sensitive, :reject_media, :reject_reports, :reject_unknown, :reason, :undo) + params.require(:domain_block).permit(:domain, :severity, :force_sensitive, :reject_media, :reject_reports, :reject_unknown, :manual_only, :reason, :undo) end end end diff --git a/app/helpers/log_helper.rb b/app/helpers/log_helper.rb index 54c741875..ae7e65868 100644 --- a/app/helpers/log_helper.rb +++ b/app/helpers/log_helper.rb @@ -7,9 +7,9 @@ module LogHelper when :create if target.is_a? DomainBlock if source.is_a? DomainBlock - LogWorker.perform_async("\xf0\x9f\x9a\xab Applied the existing #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''} policy set on '#{source.domain}' to '#{target.domain}'\u200b.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}") + LogWorker.perform_async("\xf0\x9f\x9a\xab Applied the existing #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''}#{target.manual_only? ? " and manual trust only" : ''} policy set on '#{source.domain}' to '#{target.domain}'\u200b.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}") else - LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> applied a #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''} policy on '#{target.domain}'\u200b.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}") + LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> applied a #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''}#{target.manual_only? ? " and manual trust only" : ''} policy on '#{target.domain}'\u200b.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}") end elsif target.is_a? EmailDomainBlock LogWorker.perform_async("\u26d4 <#{source}> added a registration block on email domain '#{target.domain}'.\n\nReview (moderators only): https://#{web_domain}/admin/email_domain_blocks") @@ -20,7 +20,7 @@ module LogHelper end when :destroy if target.is_a? DomainBlock - LogWorker.perform_async("\xf0\x9f\x86\x97 <#{source}> reset the policy on #{target.domain}\u200b.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}") + LogWorker.perform_async("\xf0\x9f\x86\x97 <#{source}> reset the policy on #{target.domain}\u200b.") elsif target.is_a? EmailDomainBlock LogWorker.perform_async("\xf0\x9f\x86\x97 <#{source}> removed the registration block on email domain '#{target.domain}'.") elsif target.is_a? CustomEmoji @@ -31,7 +31,7 @@ module LogHelper when :update if target.is_a? DomainBlock - LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> changed the policy on '#{target.domain}' to #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''}.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}") + LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> changed the policy on '#{target.domain}' to #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}#{target.reject_unknown? ? " and reject unknown accounts" : ''}#{target.manual_only? ? " and manual trust only" : ''}.\n\nReview (moderators only): https://#{web_domain}/admin/instances/#{target.domain}\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}") elsif target.is_a? Status LogWorker.perform_async("\xf0\x9f\x91\x81\xef\xb8\x8f <#{source}> changed visibility flags of post #{TagManager.instance.url_for(target)}\u200b.") elsif target.is_a? CustomEmoji @@ -57,6 +57,8 @@ module LogHelper else LogWorker.perform_async("\u2753 <#{source}> marked <#{target.acct}> as an unknown account.\n\n#{reason ? "Comment: #{reason}" : ''}") end + when :manual_only + LogWorker.perform_async("\u2753 <#{source}> marked <#{target.acct}> as manual trust only.\n\n#{reason ? "Comment: #{reason}" : ''}") when :force_sensitive LogWorker.perform_async("\xf0\x9f\x94\x9e <#{source}> forced the media of <#{target.acct}> to be marked sensitive.\n\n#{reason ? "Comment: #{reason}" : ''}") when :force_unlisted @@ -68,6 +70,8 @@ module LogHelper when :mark_known LogWorker.perform_async("\u2705 <#{source}> marked <#{target.acct}> as a known account.\n\n#{reason ? "Comment: #{reason}" : ''}") + when :auto_trust + LogWorker.perform_async("\u2705 <#{source}> marked <#{target.acct}> as auto-trustable.\n\n#{reason ? "Comment: #{reason}" : ''}") when :allow_nonsensitive LogWorker.perform_async("\xf0\x9f\x86\x97 <#{source}> allowed <#{target.acct}> to post media without a sensitive flag.\n\n#{reason ? "Comment: #{reason}" : ''}") when :allow_public diff --git a/app/helpers/moderation_helper.rb b/app/helpers/moderation_helper.rb index 9d12dc858..7db3ba210 100644 --- a/app/helpers/moderation_helper.rb +++ b/app/helpers/moderation_helper.rb @@ -1,22 +1,24 @@ module ModerationHelper include LogHelper - POLICIES = %w(silence unsilence suspend unsuspend force_unlisted mark_known mark_unknown reject_unknown allow_public force_sensitive allow_nonsensitive reset) + POLICIES = %w(silence unsilence suspend unsuspend force_unlisted mark_known mark_unknown reject_unknown manual_only auto_trust allow_public force_sensitive allow_nonsensitive reset) EXCLUDED_DOMAINS = %w(tailma.ws monsterpit.net monsterpit.cloud monsterpit.gallery monsterpit.blog) def janitor_account account_id = ENV.fetch('JANITOR_USER', '').to_i - return if account_id == 0 + return if account_id.zero? + Account.find_by(id: account_id) end def account_policy(username, domain, policy, reason = nil) return if policy.blank? + policy = policy.to_s return false unless policy.in?(POLICIES) username, domain = username.split('@')[1..2] if username.start_with?('@') - domain.downcase! unless domain.nil? + domain&.downcase! acct = Account.find_by(username: username, domain: domain) return false if acct.nil? @@ -34,6 +36,10 @@ module ModerationHelper acct.mark_unknown! when 'mark_known' acct.mark_known! + when 'manual_only' + acct.manual_only! + when 'auto_trust' + acct.auto_trust! when 'silence' acct.silence! when 'unsilence' @@ -61,7 +67,7 @@ module ModerationHelper acct.save - return true unless reason && !reason.strip.blank? + return true unless reason && reason.strip.present? AccountModerationNote.create( account_id: @account.id, @@ -79,11 +85,13 @@ module ModerationHelper return false end return false if [404, 410].include?(code) + true end - def domain_policy(domain, policy, reason = nil, force_sensitive: false, reject_unknown: false, reject_media: false, reject_reports: false) + def domain_policy(domain, policy, reason = nil, force_sensitive: false, reject_unknown: false, reject_media: false, manual_only: false, reject_reports: false) return if policy.blank? + policy = policy.to_s return false unless policy.in?(POLICIES) return false unless domain.match?(/\A[\w\-]+\.[\w\-]+(?:\.[\w\-]+)*\Z/) @@ -92,20 +100,21 @@ module ModerationHelper return false if domain.in?(EXCLUDED_DOMAINS) - policy = 'noop' if policy == 'force_sensitive' || policy == 'reject_unknown' + policy = 'noop' if %w(force_sensitive reject_unknown).include?(policy) + force_sensitive = true if policy == 'force_sensitive' reject_unknown = true if policy == 'reject_unknown' + manual_only = true if policy == 'manual_only' if policy.in? %w(silence suspend force_unlisted) - return false unless domain_exists?(domain) - domain_block = DomainBlock.find_or_create_by(domain: domain) domain_block.severity = policy domain_block.force_sensitive = force_sensitive domain_block.reject_unknown = reject_unknown + domain_block.manual_only = manual_only domain_block.reject_media = reject_media domain_block.reject_reports = reject_reports - domain_block.reason = reason.strip if reason && !reason.strip.blank? + domain_block.reason = reason.strip if reason && reason.strip.present? domain_block.save Admin::ActionLog.create(account: @account, action: :create, target: domain_block) diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 00951086f..d122a2860 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -45,7 +45,7 @@ module StatusesHelper def account_badge(account, all: false) content_tag(:div, class: 'roles') do froze = account.local? ? (account&.user.nil? ? true : account.user.disabled?) : account.froze? - limited = account.silenced? || account.force_unlisted? || account.force_sensitive? + limited = account.silenced? || account.force_unlisted? || account.force_sensitive? || !account.known? roles = [] roles << content_tag(:div, t('accounts.roles.limited'), class: 'account-role limited') if limited diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index 27918883f..e4de18fce 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -137,11 +137,12 @@ class ActivityPub::Activity redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, uri) end - def status_from_object(announced_by: nil) + def status_from_object(announced_by: nil, local_only: false, existing_only: false) # If the status is already known, return it status = status_from_uri(object_uri) - return status unless status.nil? + return status unless status.nil? || (local_only && !status.local?) + return if existing_only || local_only # If the boosted toot is embedded and it is a self-boost, handle it like a Create unless unsupported_object_type? @@ -188,6 +189,11 @@ class ActivityPub::Activity DomainBlock.where(domain: account.domain, reject_unknown: true).exists? end + def manual_only?(account = nil) + account = @account if account.nil? + DomainBlock.where(domain: account.domain, manual_only: true).exists? + end + def known?(account = nil) account = @account if account.nil? return true if account.known? @@ -195,6 +201,11 @@ class ActivityPub::Activity !account.service? && account.passive_relationships.exists? end + def manual_only?(account = nil) + account = @account if account.nil? + account.manual_only? + end + def reject_payload! Rails.logger.info("Rejected #{@json['type']} activity #{@json['id']} from #{@account.uri}#{@options[:relayed_through_account] && "via #{@options[:relayed_through_account].uri}"}") nil diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb index d9ff9c5b9..1942448da 100644 --- a/app/lib/activitypub/activity/add.rb +++ b/app/lib/activitypub/activity/add.rb @@ -6,7 +6,9 @@ class ActivityPub::Activity::Add < ActivityPub::Activity return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url status = status_from_uri(object_uri) - status ||= fetch_remote_original_status + if @account.known? + status ||= fetch_remote_original_status(announced_by: @account) + end return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status) diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index 714793d7a..39b05f8f1 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -3,9 +3,9 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity def perform return if autoreject? - return reject_payload! if !@options[:imported] && (delete_arrived_first?(@json['id']) || !related_to_local_activity? || !@account.known?) + return reject_payload! if !@options[:imported] && (delete_arrived_first?(@json['id']) || !related_to_local_activity?) - original_status = status_from_object(announced_by: @account) + original_status = status_from_object(announced_by: @account, local_only: !@account.known?) return reject_payload! if original_status.nil? || !announceable?(original_status) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 03a7739d8..83330cb93 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -8,7 +8,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity unless known? if @options[:announced_by].nil? return reject_payload! if !@options[:requested] && rejecting_unknown? - elsif Setting.auto_mark_known && Setting.mark_known_from_boosts && known?(@options[:announced_by]) + elsif !@account.manual_only? && Setting.auto_mark_known && Setting.mark_known_from_boosts && known?(@options[:announced_by]) @account.mark_known! else return reject_payload! diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 18b8348c7..8545ac096 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -9,7 +9,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity return if target_account.nil? || !target_account.local? || delete_arrived_first?(@json['id']) || @account.requested?(target_account) - if (rejecting_unknown? && !known?) || target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved? || target_account.instance_actor? + if !known? || target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved? || target_account.instance_actor? reject_follow_request!(target_account) return end diff --git a/app/models/account.rb b/app/models/account.rb index a2fa60a83..b0b9e9191 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -56,6 +56,7 @@ # unboostable :boolean default(FALSE), not null # block_anon :boolean default(FALSE), not null # trust_level :integer +# manual_only :boolean default(FALSE), not null # class Account < ApplicationRecord @@ -192,7 +193,7 @@ class Account < ApplicationRecord end def froze? - local? ? (self&.user.nil? ? true : user.disabled?) : froze + local? ? (self&.user.nil? ? true : user.disabled?) : froze || !known end def bot? @@ -246,11 +247,17 @@ class Account < ApplicationRecord end def mark_unknown! - update!(known: false) + known = false + avatar = nil + header = nil + self[:avatar_remote_url] = '' + self[:header_remote_url] = '' + save! end def mark_known! - update!(known: true) + update!(known: true, last_webfingered_at: nil) + refresh! unless local? || !Setting.auto_mark_instance_actors_known || domain == username _instance_actor = Account.find_remote(domain, domain) @@ -260,6 +267,14 @@ class Account < ApplicationRecord end end + def manual_only! + update!(manual_only: true) + end + + def auto_trust! + update!(manual_only: false) + end + def force_unlisted! transaction do update!(force_unlisted: true) @@ -479,7 +494,7 @@ class Account < ApplicationRecord end def can_be_marked_known? - !known && (!service || (service? && Setting.auto_mark_services_known)) && Setting.auto_mark_known + !known && !manual_only && (!service || (service? && Setting.auto_mark_services_known)) && Setting.auto_mark_known end class Field < ActiveModelSerializers::Model diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index cb6b4715a..9789cb553 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -13,6 +13,7 @@ class Admin::AccountAction silence suspend mark_unknown + manual_only ).freeze attr_accessor :target_account, @@ -69,6 +70,8 @@ class Admin::AccountAction handle_suspend! when 'mark_unknown' handle_mark_unknown! + when 'manual_only' + handle_manual_only! end end @@ -137,6 +140,12 @@ class Admin::AccountAction target_account.mark_unknown! end + def handle_manual_only! + authorize(target_account, :manual_only?) + log_action(:manual_only, target_account) + target_account.manual_only! + end + def text_for_warning [warning_preset&.text, text].compact.join("\n\n") end diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index c7395f7a3..26cec6ae6 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -14,6 +14,7 @@ # reason :text # reject_unknown :boolean default(FALSE), not null # processing :boolean default(TRUE), not null +# manual_only :boolean default(FALSE), not null # class DomainBlock < ApplicationRecord @@ -58,6 +59,7 @@ class DomainBlock < ApplicationRecord additionals << "reject media" if reject_media? additionals << "reject reports" if reject_reports? additionals << "reject unknown accounts" if reject_unknown? + additionals << "manual trust only" if manual_only? additionals end @@ -67,14 +69,15 @@ class DomainBlock < ApplicationRecord # workaround for the domain policy editor def undo - return false + false end private def set_processing return if processing - return unless (changed & %w(severity suspended_at silenced_at force_sensitive reject_media reject_reports reject_unknown)).any? + return unless (changed & %w(severity suspended_at silenced_at force_sensitive reject_media reject_reports reject_unknown manual_only)).any? + self.processing = true end end diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index 21e6e6132..b05709183 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -21,6 +21,14 @@ class AccountPolicy < ApplicationPolicy staff? end + def manual_only? + staff? + end + + def auto_trust? + staff? + end + def suspend? staff? && !record.user&.staff? end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index a81ad393d..43b05964e 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -77,7 +77,7 @@ class REST::AccountSerializer < ActiveModel::Serializer end def limited - object.silenced? || object.force_unlisted? || object.force_sensitive? + object.silenced? || object.force_unlisted? || object.force_sensitive? || !object.known? end def identity diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 4067e474f..08005f042 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -62,7 +62,8 @@ class ActivityPub::ProcessAccountService < BaseService @account.silenced_at = domain_block.created_at if auto_silence? @account.force_unlisted = true if auto_force_unlisted? @account.force_sensitive = true if auto_force_sensitive? - @account.known = @username == @domain ? Setting.always_mark_instance_actors_known : (!Setting.auto_reject_unknown && Setting.auto_mark_known) + @account.manual_only = true if auto_manual_only? + @account.known = auto_mark_known? end def update_account @@ -121,7 +122,7 @@ class ActivityPub::ProcessAccountService < BaseService end def set_reject_unknown_policy - policy = DomainBlock.create!(domain: @domain, severity: :noop, reject_unknown: true) + DomainBlock.create!(domain: @domain, severity: :noop, reject_unknown: true) user_friendly_action_log(nil, :mark_unknown, @domain) end @@ -183,6 +184,7 @@ class ActivityPub::ProcessAccountService < BaseService def property_values return unless @json['attachment'].is_a?(Array) + as_array(@json['attachment']).select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') } end @@ -223,7 +225,7 @@ class ActivityPub::ProcessAccountService < BaseService end def skip_download? - @account.suspended? || domain_block&.reject_media? + @account.suspended? || !@account.known? || domain_block&.reject_media? end def auto_suspend? @@ -242,8 +244,19 @@ class ActivityPub::ProcessAccountService < BaseService domain_block&.force_sensitive? end + def auto_manual_only? + domain_block&.manual_only? + end + + def auto_mark_known? + return false if @account.manual_only + + @username == @domain ? Setting.always_mark_instance_actors_known : (!Setting.auto_reject_unknown && Setting.auto_mark_known) + end + def domain_block return @domain_block if defined?(@domain_block) + @domain_block = DomainBlock.find_by(domain: @domain) end @@ -275,11 +288,13 @@ class ActivityPub::ProcessAccountService < BaseService as_array(@json['attachment']).each do |attachment| next unless equals_or_includes?(attachment['type'], 'IdentityProof') + current_proofs << process_identity_proof(attachment) end previous_proofs.each do |previous_proof| next if current_proofs.any? { |current_proof| current_proof.id == previous_proof.id } + previous_proof.delete end end diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb index 1fae42c50..36634fdd5 100644 --- a/app/services/block_domain_service.rb +++ b/app/services/block_domain_service.rb @@ -24,6 +24,7 @@ class BlockDomainService < BaseService clear_media! if domain_block.reject_media? || domain_block.suspend? force_accounts_sensitive! if domain_block.force_sensitive? mark_unknown_accounts! if domain_block.reject_unknown? + mark_accounts_manual_only! if domain_block.manual_only? if domain_block.force_unlisted? force_accounts_unlisted! @@ -52,8 +53,19 @@ class BlockDomainService < BaseService end end + def mark_accounts_manual_only! + blocked_domain_accounts.in_batches.update_all(manual_only: true) + end + def mark_unknown_accounts! - unknown_accounts.in_batches.update_all(known: false) + ApplicationRecord.transaction do + unknown_accounts.in_batches.update_all(known: false) + unknown_accounts.find_each do |account| + account.avatar = nil + account.header = nil + account.save! + end + end end def force_accounts_unlisted! diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index 29838ed5f..ddf52ab0c 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -16,6 +16,9 @@ class FavouriteService < BaseService return favourite unless favourite.nil? account.mark_known! if account.can_be_marked_known? && Setting.mark_known_from_favourites + + raise Mastodon::NotPermittedError("Account @#{account.acct} is restricted by an admin policy.") unless account.known? + favourite = Favourite.create!(account: account, status: status) curate_status(status) diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 68dcbda23..395b4c483 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -17,6 +17,8 @@ class FollowService < BaseService target_account.mark_known! if target_account.can_be_marked_known? && Setting.mark_known_from_follows + raise Mastodon::NotPermittedError("Account @#{target_account.acct} is restricted by an admin policy.") unless target_account.known? + SyncRemoteAccountWorker.perform_async(target_account.id) unless target_account.local? || target_account.passive_relationships.exists? if source_account.following?(target_account) diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 2d4ee4562..3e0dde72c 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -104,6 +104,7 @@ class PostStatusService < BaseService def mark_recipient_known @in_reply_to.account.mark_known! if @in_reply_to.account.can_be_marked_known? && Setting.mark_known_from_mentions + raise Mastodon::NotPermittedError("Account @#{@in_reply_to.account.acct} is restricted by an admin policy.") unless @in_reply_to.account.known? end def set_footer_from_i_am diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb index 1488a6361..bfc3766d3 100644 --- a/app/services/reblog_service.rb +++ b/app/services/reblog_service.rb @@ -11,6 +11,7 @@ class ReblogService < BaseService # @return [Status] def call(account, reblogged_status, options = {}) reblogged_status = reblogged_status.reblog if reblogged_status.reblog? + reblogged_account = reblogged_status&.account authorize_with account, reblogged_status, :reblog? @@ -18,8 +19,11 @@ class ReblogService < BaseService new_reblog = reblog.nil? if new_reblog - reblogged_status.account.mark_known! if reblogged_status.account.can_be_marked_known? && Setting.mark_known_from_boosts - reblogged_status.touch if reblogged_status.account.id == account.id + reblogged_account.mark_known! if reblogged_account.can_be_marked_known? && Setting.mark_known_from_boosts + + raise Mastodon::NotPermittedError("Account @#{reblogged_account.acct} is restricted by an admin policy.") unless reblogged_account.known? + + reblogged_status.touch if reblogged_account.id == account.id visibility = options[:visibility] || account.user&.setting_default_privacy visibility = reblogged_status.visibility if reblogged_status.hidden? diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index a6e9d4446..ddc1b26b3 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -149,6 +149,11 @@ - else = link_to t('admin.accounts.known'), mark_known_admin_account_path(@account.id), method: :post, class: 'button' if can?(:mark_known, @account) + - if @account.manual_only? + = link_to t('admin.accounts.auto_trust'), auto_trust_admin_account_path(@account.id), method: :post, class: 'button' if can?(:auto_trust, @account) + - else + = link_to t('admin.accounts.manual_only'), manual_only_admin_account_path(@account.id), method: :post, class: 'button' if can?(:manual_only, @account) + - if @account.force_sensitive? = link_to t('admin.accounts.allow_nonsensitive'), allow_nonsensitive_admin_account_path(@account.id), method: :post, class: 'button' if can?(:allow_nonsensitive, @account) - elsif !@account.local? || @account.user_approved? diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 588e512d4..da2ee5d7a 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -18,6 +18,9 @@ = f.input :reject_unknown, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_unknown'), hint: I18n.t('admin.domain_blocks.reject_unknown_hint') .fields-group + = f.input :manual_only, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.manual_only'), hint: I18n.t('admin.domain_blocks.manual_only_hint') + + .fields-group = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint') .fields-group diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml index ac13d57ab..b0856dd10 100644 --- a/app/views/admin/domain_blocks/show.html.haml +++ b/app/views/admin/domain_blocks/show.html.haml @@ -18,6 +18,9 @@ = f.input :reject_unknown, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_unknown'), hint: I18n.t('admin.domain_blocks.reject_unknown_hint') .fields-group + = f.input :manual_only, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.manual_only'), hint: I18n.t('admin.domain_blocks.manual_only_hint') + + .fields-group = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint') .fields-group diff --git a/config/locales/en.yml b/config/locales/en.yml index b19150699..87deaf6e4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -204,6 +204,8 @@ en: warn: Warn known: Mark known unknown: Mark unknown + manual_only: Manual trust + auto_trust: Auto trust web: Web action_logs: actions: @@ -245,6 +247,8 @@ en: update_status: "%{name} updated roar by %{target}" mark_known_account: "%{name} marked %{target}'s account known" mark_unknown_account: "%{name} marked %{target}'s account unknown" + manual_only_account: "%{name} marked %{target}'s account manual trust only" + auto_trust_account: "%{name} marked %{target}'s account auto-trustable" deleted_status: "(deleted roar)" title: Audit log custom_emojis: @@ -318,6 +322,8 @@ en: force_sensitive_hint: Forces all media from this domain to be marked sensitive. reject_unknown: Reject unknown accounts reject_unknown_hint: Rejects content and requests from accounts that haven't been interacted with by the community or immediate packmates. + manual_only: Manual trust only + manual_only_hint: Never automatically trust accounts from this server. reason: Add notes here. reject_media: Reject media files reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions diff --git a/config/routes.rb b/config/routes.rb index 65acaeae9..aae7e8767 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -187,6 +187,8 @@ Rails.application.routes.draw do post :enable post :mark_known post :mark_unknown + post :manual_only + post :auto_trust post :force_sensitive post :force_unlisted post :allow_public diff --git a/db/migrate/20200217052742_add_manual_only_to_domain_block.rb b/db/migrate/20200217052742_add_manual_only_to_domain_block.rb new file mode 100644 index 000000000..ae07a5504 --- /dev/null +++ b/db/migrate/20200217052742_add_manual_only_to_domain_block.rb @@ -0,0 +1,5 @@ +class AddManualOnlyToDomainBlock < ActiveRecord::Migration[5.2] + def change + safety_assured { add_column :domain_blocks, :manual_only, :boolean, default: false, null: false } + end +end diff --git a/db/migrate/20200217055054_add_manual_only_to_accounts.rb b/db/migrate/20200217055054_add_manual_only_to_accounts.rb new file mode 100644 index 000000000..3d67a09f4 --- /dev/null +++ b/db/migrate/20200217055054_add_manual_only_to_accounts.rb @@ -0,0 +1,5 @@ +class AddManualOnlyToAccounts < ActiveRecord::Migration[5.2] + def change + safety_assured { add_column(:accounts, :manual_only, :boolean, default: false, null: false) } + end +end diff --git a/db/structure.sql b/db/structure.sql index d0f9eb481..e8d415200 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -551,7 +551,8 @@ CREATE TABLE public.accounts ( force_private boolean DEFAULT false NOT NULL, unboostable boolean DEFAULT false NOT NULL, block_anon boolean DEFAULT false NOT NULL, - trust_level integer + trust_level integer, + manual_only boolean DEFAULT false NOT NULL ); @@ -973,7 +974,8 @@ CREATE TABLE public.domain_blocks ( force_sensitive boolean DEFAULT false NOT NULL, reason text, reject_unknown boolean DEFAULT false NOT NULL, - processing boolean DEFAULT true NOT NULL + processing boolean DEFAULT true NOT NULL, + manual_only boolean DEFAULT false NOT NULL ); @@ -2225,7 +2227,8 @@ CREATE TABLE public.statuses ( boostable boolean, reject_replies boolean, tsv tsvector GENERATED ALWAYS AS (to_tsvector('public.fedi'::regconfig, public.f_strip_mentions(((spoiler_text || ' '::text) || text)))) STORED, - hidden boolean + hidden boolean, + CONSTRAINT statuses_hidden_null CHECK ((hidden IS NOT NULL)) ); @@ -5415,6 +5418,12 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200115201524'), ('20200205194250'), ('20200215014558'), -('20200215020121'); +('20200215020121'), +('20200215021014'), +('20200215021731'), +('20200215021732'), +('20200216000613'), +('20200217052742'), +('20200217055054'); |