add new `reject unknown` policy option to prevent spam & harassment from large/undermoderated servers
parent
d019e55b7b
commit
4dfc40324b
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Admin
|
||||
class AccountsController < BaseController
|
||||
before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :allow_public, :allow_nonsensitive, :unsilence, :unsuspend, :memorialize, :approve, :reject]
|
||||
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]
|
||||
before_action :require_remote_account!, only: [:redownload]
|
||||
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
|
||||
|
||||
|
@ -45,6 +45,20 @@ module Admin
|
|||
redirect_to admin_accounts_path(pending: '1')
|
||||
end
|
||||
|
||||
def mark_unknown
|
||||
authorize @account, :mark_unknown?
|
||||
@account.mark_unknown!
|
||||
log_action :mark_unknown, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def mark_known
|
||||
authorize @account, :mark_known?
|
||||
@account.mark_known!
|
||||
log_action :mark_known, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def force_sensitive
|
||||
authorize @account, :force_sensitive?
|
||||
@account.force_sensitive!
|
||||
|
|
|
@ -50,7 +50,7 @@ module Admin
|
|||
@domain_block.update(resource_params.except(:domain, :undo))
|
||||
changed = @domain_block.changed
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id) if (changed & %w(severity force_sensitive reject_media)).any?
|
||||
DomainBlockWorker.perform_async(@domain_block.id) if (changed & %w(severity force_sensitive reject_media reject_unknown)).any?
|
||||
log_action :update, @domain_block
|
||||
flash[:notice] = I18n.t('admin.domain_blocks.updated_msg')
|
||||
else
|
||||
|
@ -66,7 +66,7 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity, :force_sensitive, :reject_media, :reject_reports, :reason, :undo)
|
||||
params.require(:domain_block).permit(:domain, :severity, :force_sensitive, :reject_media, :reject_reports, :reject_unknown, :reason, :undo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module LogHelper
|
|||
case action
|
||||
when :create
|
||||
if target.is_a? DomainBlock
|
||||
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" : ''} policy on https://#{target.domain}\u200b.\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" : ''} policy on https://#{target.domain}\u200b.\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}")
|
||||
elsif target.is_a? EmailDomainBlock
|
||||
LogWorker.perform_async("\u26d4 <#{source}> added a registration block on email domain '#{target.domain}'.")
|
||||
elsif target.is_a? CustomEmoji
|
||||
|
@ -26,7 +26,7 @@ module LogHelper
|
|||
|
||||
when :update
|
||||
if target.is_a? DomainBlock
|
||||
LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> changed the policy on https://#{target.domain} to #{target.severity}#{target.force_sensitive? ? " and force sensitive media" : ''}#{target.reject_media? ? " and reject media" : ''}.\n\n#{target.reason? ? "Comment: #{target.reason}" : ''}")
|
||||
LogWorker.perform_async("\xf0\x9f\x9a\xab <#{source}> changed the policy on https://#{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\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
|
||||
|
@ -46,17 +46,21 @@ module LogHelper
|
|||
LogWorker.perform_async("\u26d4 <#{source}> disabled the '#{target.shortcode}' emoji.")
|
||||
end
|
||||
|
||||
when :mark_unknown
|
||||
LogWorker.perform_async("\u2753 <#{source}> marked <#{target.acct}> as an unknown account.\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
|
||||
LogWorker.perform_async("\xf0\x9f\x94\x89 <#{source}> forced the posts of <#{target.acct}> to be unlisted.\n\n#{reason ? "Comment: #{reason}" : ''}")
|
||||
when :silence
|
||||
LogWorker.perform_async("\xf0\x9f\x94\x87 <#{source}> silenced <#{target.acct}>'.\n\n#{reason ? "Comment: #{reason}" : ''}")
|
||||
LogWorker.perform_async("\xf0\x9f\x94\x87 <#{source}> silenced <#{target.acct}>.\n\n#{reason ? "Comment: #{reason}" : ''}")
|
||||
when :suspend
|
||||
LogWorker.perform_async("\u26d4 <#{source}> suspended <#{target.acct}>.\n\n#{reason ? "Comment: #{reason}" : ''}")
|
||||
|
||||
when :mark_known
|
||||
LogWorker.perform_async("\u2705 <#{source}> marked <#{target.acct}> as a known account.\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.")
|
||||
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
|
||||
LogWorker.perform_async("\xf0\x9f\x86\x8a <#{source}> allowed <#{target.acct}> to post with public visibility.")
|
||||
when :unsilence
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module ModerationHelper
|
||||
include LogHelper
|
||||
|
||||
POLICIES = %w(silence unsilence suspend unsuspend force_unlisted allow_public force_sensitive allow_nonsensitive reset)
|
||||
POLICIES = %w(silence unsilence suspend unsuspend force_unlisted mark_known mark_unknown reject_unknown allow_public force_sensitive allow_nonsensitive reset)
|
||||
EXCLUDED_DOMAINS = %w(tailma.ws monsterpit.net monsterpit.cloud monsterpit.gallery monsterpit.blog)
|
||||
|
||||
def janitor_account
|
||||
|
@ -30,6 +30,10 @@ module ModerationHelper
|
|||
end
|
||||
|
||||
case policy
|
||||
when 'mark_unknown', 'reject_unknown'
|
||||
acct.mark_unknown!
|
||||
when 'mark_known'
|
||||
acct.mark_known!
|
||||
when 'silence'
|
||||
acct.silence!
|
||||
when 'unsilence'
|
||||
|
@ -52,6 +56,7 @@ module ModerationHelper
|
|||
acct.unsilence!
|
||||
acct.allow_public!
|
||||
acct.allow_nonsensitive!
|
||||
acct.mark_known!
|
||||
end
|
||||
|
||||
acct.save
|
||||
|
@ -77,7 +82,7 @@ module ModerationHelper
|
|||
true
|
||||
end
|
||||
|
||||
def domain_policy(domain, policy, reason = nil, force_sensitive = false, reject_media = false, reject_reports = false)
|
||||
def domain_policy(domain, policy, reason = nil, force_sensitive: false, reject_unknown: false, reject_media: false, reject_reports: false)
|
||||
return if policy.blank?
|
||||
policy = policy.to_s
|
||||
return false unless policy.in?(POLICIES)
|
||||
|
@ -87,10 +92,9 @@ module ModerationHelper
|
|||
|
||||
return false if domain.in?(EXCLUDED_DOMAINS)
|
||||
|
||||
if policy == 'force_sensitive'
|
||||
policy = 'noop'
|
||||
force_sensitive = true
|
||||
end
|
||||
policy = 'noop' if policy == 'force_sensitive' || policy == 'reject_unknown'
|
||||
force_sensitive = true if policy == 'force_sensitive'
|
||||
reject_unknown = true if policy == 'reject_unknown'
|
||||
|
||||
if policy.in? %w(silence suspend force_unlisted)
|
||||
return false unless domain_exists?(domain)
|
||||
|
@ -98,6 +102,7 @@ module ModerationHelper
|
|||
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.reject_media = reject_media
|
||||
domain_block.reject_reports = reject_reports
|
||||
domain_block.reason = reason.strip if reason && !reason.strip.blank?
|
||||
|
|
|
@ -137,7 +137,7 @@ class ActivityPub::Activity
|
|||
redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, uri)
|
||||
end
|
||||
|
||||
def status_from_object
|
||||
def status_from_object(announced_by: nil)
|
||||
# If the status is already known, return it
|
||||
status = status_from_uri(object_uri)
|
||||
|
||||
|
@ -148,19 +148,20 @@ class ActivityPub::Activity
|
|||
actor_id = value_or_id(first_of_value(@object['attributedTo'])) || @account.uri
|
||||
|
||||
if actor_id == @account.uri
|
||||
return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
|
||||
return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account, announced_by: announced_by).perform
|
||||
end
|
||||
end
|
||||
|
||||
fetch_remote_original_status
|
||||
fetch_remote_original_status(announced_by: announced_by)
|
||||
end
|
||||
|
||||
def fetch_remote_original_status
|
||||
def fetch_remote_original_status(announced_by: nil)
|
||||
if object_uri.start_with?('http')
|
||||
return if ActivityPub::TagManager.instance.local_uri?(object_uri)
|
||||
ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first)
|
||||
ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first, announced_by: announced_by)
|
||||
elsif @object['url'].present?
|
||||
::FetchRemoteStatusService.new.call(@object['url'])
|
||||
options[:id] = true
|
||||
::FetchRemoteStatusService.new.call(@object['url'], announced_by: announced_by)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -182,6 +183,17 @@ class ActivityPub::Activity
|
|||
@options[:relayed_through_account] && Relay.find_by(inbox_url: @options[:relayed_through_account].inbox_url)&.enabled?
|
||||
end
|
||||
|
||||
def rejecting_unknown?(account = nil)
|
||||
account = @account if account.nil?
|
||||
DomainBlock.where(domain: account.domain, reject_unknown: true).exists?
|
||||
end
|
||||
|
||||
def known?(account = nil)
|
||||
account = @account if account.nil?
|
||||
return true if account.known?
|
||||
account.passive_relationships.exists?
|
||||
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
|
||||
|
|
|
@ -5,7 +5,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
|
|||
return if autoreject?
|
||||
return reject_payload! if !@options[:imported] && (delete_arrived_first?(@json['id']) || !related_to_local_activity?)
|
||||
|
||||
original_status = status_from_object
|
||||
original_status = status_from_object(announced_by: @account)
|
||||
|
||||
return reject_payload! if original_status.nil? || !announceable?(original_status)
|
||||
|
||||
|
|
|
@ -5,6 +5,14 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
return if autoreject?
|
||||
return reject_payload! if unsupported_object_type? || !@options[:imported] && (invalid_origin?(@object['id']) || Tombstone.exists?(uri: @object['id']) || !related_to_local_activity?)
|
||||
|
||||
unless known?
|
||||
if @options[:announced_by].nil?
|
||||
return reject_payload! if !@options[:requested] && rejecting_unknown?
|
||||
else
|
||||
@account.mark_known! if known?(@options[:announced_by])
|
||||
end
|
||||
end
|
||||
|
||||
RedisLock.acquire(lock_options) do |lock|
|
||||
if lock.acquired?
|
||||
return if !@options[:imported] && (delete_arrived_first?(object_uri) || poll_vote?)
|
||||
|
|
|
@ -7,7 +7,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 target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved?
|
||||
if (rejecting_unknown? && !known?) || target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved?
|
||||
reject_follow_request!(target_account)
|
||||
return
|
||||
end
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
# gently :boolean default(FALSE), not null
|
||||
# kobold :boolean default(FALSE), not null
|
||||
# froze :boolean
|
||||
# known :boolean default(FALSE), not null
|
||||
#
|
||||
|
||||
class Account < ApplicationRecord
|
||||
|
@ -213,6 +214,14 @@ class Account < ApplicationRecord
|
|||
ResolveAccountService.new.call(acct)
|
||||
end
|
||||
|
||||
def mark_unknown!
|
||||
update!(known: false)
|
||||
end
|
||||
|
||||
def mark_known!
|
||||
update!(known: true)
|
||||
end
|
||||
|
||||
def force_unlisted!
|
||||
transaction do
|
||||
update!(force_unlisted: true)
|
||||
|
@ -557,6 +566,7 @@ class Account < ApplicationRecord
|
|||
|
||||
before_create :generate_keys
|
||||
before_create :set_domain_from_inbox_url
|
||||
before_create :set_known, if: :local?
|
||||
before_validation :prepare_contents, if: :local?
|
||||
before_validation :prepare_username, on: :create
|
||||
before_destroy :clean_feed_manager
|
||||
|
@ -579,6 +589,10 @@ class Account < ApplicationRecord
|
|||
nil
|
||||
end
|
||||
|
||||
def set_known
|
||||
self.known = true
|
||||
end
|
||||
|
||||
def generate_keys
|
||||
return unless local? && !Rails.env.test?
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#
|
||||
|
||||
class AccountWarning < ApplicationRecord
|
||||
enum action: %i(none disable force_sensitive force_unlisted silence suspend), _suffix: :action
|
||||
enum action: %i(none disable force_sensitive force_unlisted silence suspend mark_unknown), _suffix: :action
|
||||
|
||||
belongs_to :account, inverse_of: :account_warnings
|
||||
belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings
|
||||
|
|
|
@ -12,6 +12,7 @@ class Admin::AccountAction
|
|||
force_unlisted
|
||||
silence
|
||||
suspend
|
||||
mark_unknown
|
||||
).freeze
|
||||
|
||||
attr_accessor :target_account,
|
||||
|
@ -66,6 +67,8 @@ class Admin::AccountAction
|
|||
handle_silence!
|
||||
when 'suspend'
|
||||
handle_suspend!
|
||||
when 'mark_unknown'
|
||||
handle_mark_unknown!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -128,6 +131,12 @@ class Admin::AccountAction
|
|||
queue_suspension_worker!
|
||||
end
|
||||
|
||||
def handle_mark_unknown!
|
||||
authorize(target_account, :mark_unknown?)
|
||||
log_action(:mark_unknown, target_account.user)
|
||||
target_account.mark_unknown!
|
||||
end
|
||||
|
||||
def text_for_warning
|
||||
[warning_preset&.text, text].compact.join("\n\n")
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# reject_reports :boolean default(FALSE), not null
|
||||
# force_sensitive :boolean default(FALSE), not null
|
||||
# reason :text
|
||||
# reject_unknown :boolean default(FALSE), not null
|
||||
#
|
||||
|
||||
class DomainBlock < ApplicationRecord
|
||||
|
@ -52,6 +53,7 @@ class DomainBlock < ApplicationRecord
|
|||
additionals << "force sensitive media" if force_sensitive?
|
||||
additionals << "reject media" if reject_media?
|
||||
additionals << "reject reports" if reject_reports?
|
||||
additionals << "reject unknown accounts" if reject_unknown?
|
||||
additionals
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,14 @@ class AccountPolicy < ApplicationPolicy
|
|||
staff? && !record.user&.staff?
|
||||
end
|
||||
|
||||
def mark_known?
|
||||
staff?
|
||||
end
|
||||
|
||||
def mark_unknown?
|
||||
staff?
|
||||
end
|
||||
|
||||
def suspend?
|
||||
staff? && !record.user&.staff?
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService
|
|||
include AutorejectHelper
|
||||
|
||||
# Should be called when uri has already been checked for locality
|
||||
def call(uri, id: true, prefetched_body: nil, on_behalf_of: nil)
|
||||
def call(uri, id: true, prefetched_body: nil, on_behalf_of: nil, announced_by: nil, requested: false)
|
||||
return if autoreject?(uri)
|
||||
|
||||
@json = if prefetched_body.nil?
|
||||
|
@ -24,7 +24,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService
|
|||
|
||||
return if actor.nil? || actor.suspended?
|
||||
|
||||
ActivityPub::Activity.factory(activity_json, actor).perform
|
||||
ActivityPub::Activity.factory(activity_json, actor, announced_by: announced_by, requested: requested).perform
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -5,8 +5,11 @@ class BlockDomainService < BaseService
|
|||
|
||||
def call(domain_block)
|
||||
@domain_block = domain_block
|
||||
@affected_status_ids = []
|
||||
|
||||
remove_existing_block!
|
||||
process_domain_block!
|
||||
invalidate_association_caches!
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -16,8 +19,9 @@ class BlockDomainService < BaseService
|
|||
end
|
||||
|
||||
def process_domain_block!
|
||||
clear_media! if domain_block.reject_media?
|
||||
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?
|
||||
|
||||
if domain_block.force_unlisted?
|
||||
force_accounts_unlisted!
|
||||
|
@ -39,17 +43,22 @@ class BlockDomainService < BaseService
|
|||
def force_accounts_sensitive!
|
||||
ApplicationRecord.transaction do
|
||||
blocked_domain_accounts.in_batches.update_all(force_sensitive: true)
|
||||
blocked_domain_accounts.reorder(nil).find_each do |account|
|
||||
blocked_domain_accounts.find_each do |account|
|
||||
@affected_status_ids |= account.statuses.where(sensitive: false).pluck(:id)
|
||||
account.statuses.where(sensitive: false).in_batches.update_all(sensitive: true)
|
||||
end
|
||||
end
|
||||
invalidate_association_caches! unless @affected_status_ids.blank?
|
||||
end
|
||||
|
||||
def mark_unknown_accounts!
|
||||
unknown_accounts.in_batches.update_all(known: false)
|
||||
end
|
||||
|
||||
def force_accounts_unlisted!
|
||||
ApplicationRecord.transaction do
|
||||
blocked_domain_accounts.in_batches.update_all(force_unlisted: true)
|
||||
blocked_domain_accounts.reorder(nil).find_each do |account|
|
||||
blocked_domain_accounts.find_each do |account|
|
||||
@affected_status_ids |= account.statuses.with_public_visibility.pluck(:id)
|
||||
account.statuses.with_public_visibility.in_batches.update_all(visibility: :unlisted)
|
||||
end
|
||||
end
|
||||
|
@ -60,23 +69,21 @@ class BlockDomainService < BaseService
|
|||
end
|
||||
|
||||
def clear_media!
|
||||
@affected_status_ids = []
|
||||
|
||||
clear_account_images!
|
||||
clear_account_attachments!
|
||||
clear_emojos!
|
||||
|
||||
invalidate_association_caches!
|
||||
end
|
||||
|
||||
def suspend_accounts!
|
||||
blocked_domain_accounts.without_suspended.reorder(nil).find_each do |account|
|
||||
blocked_domain_accounts.without_suspended.find_each do |account|
|
||||
SuspendAccountService.new.call(account, suspended_at: @domain_block.created_at)
|
||||
end
|
||||
end
|
||||
|
||||
def clear_account_images!
|
||||
blocked_domain_accounts.reorder(nil).find_each do |account|
|
||||
blocked_domain_accounts.find_each do |account|
|
||||
account.avatar.destroy if account.avatar.exists?
|
||||
account.header.destroy if account.header.exists?
|
||||
account.save
|
||||
|
@ -84,7 +91,7 @@ class BlockDomainService < BaseService
|
|||
end
|
||||
|
||||
def clear_account_attachments!
|
||||
media_from_blocked_domain.reorder(nil).find_each do |attachment|
|
||||
media_from_blocked_domain.find_each do |attachment|
|
||||
@affected_status_ids << attachment.status_id if attachment.status_id.present?
|
||||
|
||||
attachment.file.destroy if attachment.file.exists?
|
||||
|
@ -102,7 +109,7 @@ class BlockDomainService < BaseService
|
|||
end
|
||||
|
||||
def blocked_domain_accounts
|
||||
Account.where(domain: blocked_domain)
|
||||
Account.where(domain: blocked_domain).reorder(nil)
|
||||
end
|
||||
|
||||
def media_from_blocked_domain
|
||||
|
@ -112,4 +119,28 @@ class BlockDomainService < BaseService
|
|||
def emojis_from_blocked_domains
|
||||
CustomEmoji.where(domain: blocked_domain)
|
||||
end
|
||||
|
||||
def unknown_accounts
|
||||
Account.where(id: blocked_domain_accounts.pluck(:id) - known_account_ids).reorder(nil)
|
||||
end
|
||||
|
||||
def known_account_ids
|
||||
local_accounts | packmates | boosted_authors | faved_authors
|
||||
end
|
||||
|
||||
def boosted_authors
|
||||
Status.where(id: Status.local.reblogs.reorder(nil).select(:reblog_of_id)).reorder(nil).pluck(:account_id)
|
||||
end
|
||||
|
||||
def faved_authors
|
||||
Status.where(id: Favourite.select(:status_id)).reorder(nil).pluck(:account_id)
|
||||
end
|
||||
|
||||
def local_accounts
|
||||
Account.local.pluck(:id)
|
||||
end
|
||||
|
||||
def packmates
|
||||
Account.local.flat_map { |account| account.following_ids | account.follower_ids }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,8 @@ class FavouriteService < BaseService
|
|||
|
||||
favourite = Favourite.create!(account: account, status: status)
|
||||
|
||||
status.account.mark_known! unless status.account.known?
|
||||
|
||||
curate_status(status)
|
||||
create_notification(favourite) unless skip_notify
|
||||
bump_potential_friendship(account, status)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FetchRemoteStatusService < BaseService
|
||||
def call(url, prefetched_body = nil)
|
||||
def call(url, prefetched_body = nil, announced_by: nil, requested: false)
|
||||
if prefetched_body.nil?
|
||||
resource_url, resource_options = FetchAtomService.new.call(url)
|
||||
else
|
||||
|
@ -9,6 +9,9 @@ class FetchRemoteStatusService < BaseService
|
|||
resource_options = { prefetched_body: prefetched_body }
|
||||
end
|
||||
|
||||
resource_options[:announced_by] = announced_by unless announced_by.nil?
|
||||
resource_options[:requested] = true if requested
|
||||
|
||||
return if resource_url.blank?
|
||||
ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options)
|
||||
end
|
||||
|
|
|
@ -14,6 +14,8 @@ class FollowService < BaseService
|
|||
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
|
||||
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || target_account.moved?
|
||||
|
||||
target_account.mark_known! unless target_account.known?
|
||||
|
||||
if source_account.following?(target_account)
|
||||
# We're already following this account, but we'll call follow! again to
|
||||
# make sure the reblogs status is set correctly.
|
||||
|
|
|
@ -94,6 +94,10 @@ class PostStatusService < BaseService
|
|||
@in_reply_to.present? && @in_reply_to.reject_replies && @in_reply_to.account_id != @account.id
|
||||
end
|
||||
|
||||
def mark_recipient_known
|
||||
@in_reply_to.account.mark_known! unless @in_reply_to.account.known?
|
||||
end
|
||||
|
||||
def set_footer_from_i_am
|
||||
return if @footer.present? || @options[:no_footer]
|
||||
name = @account.user.vars['_they:are']
|
||||
|
@ -153,6 +157,7 @@ class PostStatusService < BaseService
|
|||
limit_visibility_if_silenced
|
||||
|
||||
unless @in_reply_to.nil?
|
||||
mark_recipient_known
|
||||
inherit_reply_rejection
|
||||
limit_visibility_to_reply
|
||||
unfilter_thread_on_reply
|
||||
|
|
|
@ -17,6 +17,8 @@ class ReblogService < BaseService
|
|||
new_reblog = reblog.nil?
|
||||
|
||||
if new_reblog
|
||||
reblogged_status.account.mark_known! unless reblogged_status.account.known?
|
||||
|
||||
visibility = options[:visibility] || account.user&.setting_default_privacy
|
||||
visibility = reblogged_status.visibility if reblogged_status.hidden?
|
||||
reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility)
|
||||
|
|
|
@ -21,7 +21,7 @@ class ResolveURLService < BaseService
|
|||
if equals_or_includes_any?(type, %w(Application Group Organization Person Service))
|
||||
FetchRemoteAccountService.new.call(atom_url, body)
|
||||
elsif equals_or_includes_any?(type, %w(Note Article Image Video Page Question))
|
||||
FetchRemoteStatusService.new.call(atom_url, body)
|
||||
FetchRemoteStatusService.new.call(atom_url, body, requested: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -144,6 +144,11 @@
|
|||
- if @account.local? && @account.user_approved?
|
||||
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
|
||||
|
||||
- if @account.known?
|
||||
= link_to t('admin.accounts.unknown'), mark_unknown_admin_account_path(@account.id), method: :post, class: 'button' if can?(:mark_unknown, @account)
|
||||
- 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.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?
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
.fields-group
|
||||
= f.input :force_sensitive, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.force_sensitive'), hint: I18n.t('admin.domain_blocks.force_sensitive_hint')
|
||||
|
||||
.fields-group
|
||||
= 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 :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
.fields-group
|
||||
= f.input :force_sensitive, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.force_sensitive'), hint: I18n.t('admin.domain_blocks.force_sensitive_hint')
|
||||
|
||||
.fields-group
|
||||
= 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 :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')
|
||||
|
||||
|
|
|
@ -196,6 +196,8 @@ en:
|
|||
unsubscribe: Unsubscribe
|
||||
username: Username
|
||||
warn: Warn
|
||||
known: Mark known
|
||||
unknown: Mark unknown
|
||||
web: Web
|
||||
action_logs:
|
||||
actions:
|
||||
|
@ -235,6 +237,8 @@ en:
|
|||
update_custom_emoji: "%{name} updated emoji %{target}"
|
||||
update_domain_block: "%{name} updated policy for %{target}"
|
||||
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"
|
||||
deleted_status: "(deleted roar)"
|
||||
title: Audit log
|
||||
custom_emojis:
|
||||
|
@ -305,6 +309,8 @@ en:
|
|||
title: New domain policy
|
||||
force_sensitive: Mark media sensitive
|
||||
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.
|
||||
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
|
||||
|
|
|
@ -70,6 +70,7 @@ en:
|
|||
none: Do nothing
|
||||
force_sensitive: Force sensitive
|
||||
force_unlisted: Force unlisted
|
||||
mark_unknown: Mark unknown
|
||||
silence: Silence
|
||||
suspend: Suspend and irreversibly delete account data
|
||||
warning_preset_id: Use a warning preset
|
||||
|
|
|
@ -189,6 +189,8 @@ Rails.application.routes.draw do
|
|||
post :subscribe
|
||||
post :unsubscribe
|
||||
post :enable
|
||||
post :mark_known
|
||||
post :mark_unknown
|
||||
post :force_sensitive
|
||||
post :force_unlisted
|
||||
post :allow_public
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddKnownToAccounts < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :accounts, :known, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
class MarkKnownAccounts < ActiveRecord::Migration[5.2]
|
||||
def up
|
||||
Rails.logger.info("Marking known accounts:")
|
||||
known_accounts = local_accounts | packmates | boosted_authors | faved_authors
|
||||
Rails.logger.info(" Updating account flags...")
|
||||
Account.where(id: known_accounts).in_batches.update_all(known: true)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def boosted_authors
|
||||
Rails.logger.info(" Gathering boosted accounts...")
|
||||
Status.where(id: Status.local.reblogs.reorder(nil).select(:reblog_of_id)).reorder(nil).pluck(:account_id)
|
||||
end
|
||||
|
||||
def faved_authors
|
||||
Rails.logger.info(" Gathering favourited accounts...")
|
||||
Status.where(id: Favourite.select(:status_id)).reorder(nil).pluck(:account_id)
|
||||
end
|
||||
|
||||
def local_accounts
|
||||
Rails.logger.info(" Gathering local accounts...")
|
||||
Account.local.pluck(:id)
|
||||
end
|
||||
|
||||
def packmates
|
||||
Rails.logger.info(" Gathering packmate accounts...")
|
||||
Account.local.flat_map { |account| account.following_ids | account.follower_ids }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class AddRejectUnknownToDomainBlocks < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
safety_assured {
|
||||
add_column :domain_blocks, :reject_unknown, :boolean, null: false, default: false
|
||||
}
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2019_08_06_195913) do
|
||||
ActiveRecord::Schema.define(version: 2019_08_07_221924) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -154,6 +154,7 @@ ActiveRecord::Schema.define(version: 2019_08_06_195913) do
|
|||
t.boolean "gently", default: false, null: false
|
||||
t.boolean "kobold", default: false, null: false
|
||||
t.boolean "froze"
|
||||
t.boolean "known", default: false, null: false
|
||||
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
|
||||
t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true
|
||||
t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id"
|
||||
|
@ -274,6 +275,7 @@ ActiveRecord::Schema.define(version: 2019_08_06_195913) do
|
|||
t.boolean "reject_reports", default: false, null: false
|
||||
t.boolean "force_sensitive", default: false, null: false
|
||||
t.text "reason"
|
||||
t.boolean "reject_unknown", default: false, null: false
|
||||
t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue