diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index d486a97ba..9054a8aca 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, :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! diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index afba387cc..587c2c8c2 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -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 diff --git a/app/helpers/log_helper.rb b/app/helpers/log_helper.rb index 550ed2c15..6d0fdac13 100644 --- a/app/helpers/log_helper.rb +++ b/app/helpers/log_helper.rb @@ -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 diff --git a/app/helpers/moderation_helper.rb b/app/helpers/moderation_helper.rb index fda553c37..2f1561772 100644 --- a/app/helpers/moderation_helper.rb +++ b/app/helpers/moderation_helper.rb @@ -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? diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index fee8fcd25..a708925a6 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -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 diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index ada46b378..245126123 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -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) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 140226481..37aac4d59 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -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?) diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 84041ec8d..bde507dcf 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -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 diff --git a/app/models/account.rb b/app/models/account.rb index 6528a7ae9..97b0e93e4 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -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? diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 4e06cf3d0..ddc3ff96e 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -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 diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index 1ed464423..2195dc6a9 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -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 diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index e4baee5f0..7d3b65720 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -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 diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index f3bda83db..35f9b98b3 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -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 diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 42280ad74..423c7bc9a 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -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 diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb index 908deacf4..5a6f4a6ce 100644 --- a/app/services/block_domain_service.rb +++ b/app/services/block_domain_service.rb @@ -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 diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index ee3248877..ce13b92ed 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -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) diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb index 2c6dbb58d..4fde2b395 100644 --- a/app/services/fetch_remote_status_service.rb +++ b/app/services/fetch_remote_status_service.rb @@ -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 diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 3494dce99..776c4cb9b 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -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. diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index bd076eaf4..6377248e5 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -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 diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb index 7d72357f9..1e7d4f3ca 100644 --- a/app/services/reblog_service.rb +++ b/app/services/reblog_service.rb @@ -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) diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 89a31c9ed..0c265b0db 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -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 diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 0066ed8e7..f89b72422 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -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? diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index d925edd64..588e512d4 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -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') diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml index 02c73c268..ac13d57ab 100644 --- a/app/views/admin/domain_blocks/show.html.haml +++ b/app/views/admin/domain_blocks/show.html.haml @@ -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') diff --git a/config/locales/en.yml b/config/locales/en.yml index 95b20571a..6f34635f3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 28750bcda..809c5dee8 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index e716ab383..97a6821b3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20190807171841_add_known_to_accounts.rb b/db/migrate/20190807171841_add_known_to_accounts.rb new file mode 100644 index 000000000..ff43f2a32 --- /dev/null +++ b/db/migrate/20190807171841_add_known_to_accounts.rb @@ -0,0 +1,5 @@ +class AddKnownToAccounts < ActiveRecord::Migration[5.2] + def change + add_column :accounts, :known, :boolean, null: false, default: false + end +end diff --git a/db/migrate/20190807172051_mark_known_accounts.rb b/db/migrate/20190807172051_mark_known_accounts.rb new file mode 100644 index 000000000..95f9fe99c --- /dev/null +++ b/db/migrate/20190807172051_mark_known_accounts.rb @@ -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 diff --git a/db/migrate/20190807221924_add_reject_unknown_to_domain_blocks.rb b/db/migrate/20190807221924_add_reject_unknown_to_domain_blocks.rb new file mode 100644 index 000000000..110d1fb79 --- /dev/null +++ b/db/migrate/20190807221924_add_reject_unknown_to_domain_blocks.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index a57ac2fd3..7f7d31bd2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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