Add ability for admins to delete canonical email blocks (#16644)

* Add admin option to remove canonical email blocks from a deleted account

* Add tootctl canonical_email_blocks to inspect and remove canonical email blocks
This commit is contained in:
Claire 2021-12-17 23:02:14 +01:00 committed by GitHub
parent 7f803c41e2
commit 76761d5fc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 128 additions and 2 deletions

View File

@ -117,6 +117,16 @@ module Admin
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct)
end end
def unblock_email
authorize @account, :unblock_email?
CanonicalEmailBlock.where(reference_account: @account).delete_all
log_action :unblock_email, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unblocked_email_msg', username: @account.acct)
end
private private
def set_account def set_account

View File

@ -50,6 +50,7 @@ class Admin::ActionLogFilter
update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, update_announcement: { target_type: 'Announcement', action: 'update' }.freeze,
update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze, update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze,
update_status: { target_type: 'Status', action: 'update' }.freeze, update_status: { target_type: 'Status', action: 'update' }.freeze,
unblock_email_account: { target_type: 'Account', action: 'unblock_email' }.freeze,
}.freeze }.freeze
attr_reader :params attr_reader :params

View File

@ -24,4 +24,8 @@ class CanonicalEmailBlock < ApplicationRecord
def self.block?(email) def self.block?(email)
where(canonical_email_hash: email_to_canonical_email_hash(email)).exists? where(canonical_email_hash: email_to_canonical_email_hash(email)).exists?
end end
def self.find_blocks(email)
where(canonical_email_hash: email_to_canonical_email_hash(email))
end
end end

View File

@ -64,4 +64,8 @@ class AccountPolicy < ApplicationPolicy
def memorialize? def memorialize?
admin? && !record.user&.admin? && !record.instance_actor? admin? && !record.user&.admin? && !record.instance_actor?
end end
def unblock_email?
staff?
end
end end

View File

@ -71,7 +71,9 @@
= t('admin.accounts.no_limits_imposed') = t('admin.accounts.no_limits_imposed')
.dashboard__counters__label= t 'admin.accounts.login_status' .dashboard__counters__label= t 'admin.accounts.login_status'
- unless @account.local? && @account.user.nil? - if @account.local? && @account.user.nil?
= link_to t('admin.accounts.unblock_email'), unblock_email_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unblock_email, @account) && CanonicalEmailBlock.where(reference_account_id: @account.id).exists?
- else
.table-wrapper .table-wrapper
%table.table.inline-table %table.table.inline-table
%tbody %tbody

View File

@ -208,6 +208,8 @@ en:
suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had. suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had.
suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below. suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below.
title: Accounts title: Accounts
unblock_email: Unblock email address
unblocked_email_msg: Successfully unblocked %{username}'s email address
unconfirmed_email: Unconfirmed email unconfirmed_email: Unconfirmed email
undo_sensitized: Undo force-sensitive undo_sensitized: Undo force-sensitive
undo_silenced: Undo limit undo_silenced: Undo limit
@ -262,6 +264,7 @@ en:
silence_account: Limit Account silence_account: Limit Account
suspend_account: Suspend Account suspend_account: Suspend Account
unassigned_report: Unassign Report unassigned_report: Unassign Report
unblock_email_account: Unblock email address
unsensitive_account: Undo Force-Sensitive Account unsensitive_account: Undo Force-Sensitive Account
unsilence_account: Undo Limit Account unsilence_account: Undo Limit Account
unsuspend_account: Unsuspend Account unsuspend_account: Unsuspend Account
@ -310,6 +313,7 @@ en:
silence_account_html: "%{name} limited %{target}'s account" silence_account_html: "%{name} limited %{target}'s account"
suspend_account_html: "%{name} suspended %{target}'s account" suspend_account_html: "%{name} suspended %{target}'s account"
unassigned_report_html: "%{name} unassigned report %{target}" unassigned_report_html: "%{name} unassigned report %{target}"
unblock_email_account_html: "%{name} unblocked %{target}'s email address"
unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive" unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive"
unsilence_account_html: "%{name} undid limit of %{target}'s account" unsilence_account_html: "%{name} undid limit of %{target}'s account"
unsuspend_account_html: "%{name} unsuspended %{target}'s account" unsuspend_account_html: "%{name} unsuspended %{target}'s account"

View File

@ -249,6 +249,7 @@ Rails.application.routes.draw do
post :memorialize post :memorialize
post :approve post :approve
post :reject post :reject
post :unblock_email
end end
collection do collection do

View File

@ -13,6 +13,7 @@ require_relative 'mastodon/preview_cards_cli'
require_relative 'mastodon/cache_cli' require_relative 'mastodon/cache_cli'
require_relative 'mastodon/upgrade_cli' require_relative 'mastodon/upgrade_cli'
require_relative 'mastodon/email_domain_blocks_cli' require_relative 'mastodon/email_domain_blocks_cli'
require_relative 'mastodon/canonical_email_blocks_cli'
require_relative 'mastodon/ip_blocks_cli' require_relative 'mastodon/ip_blocks_cli'
require_relative 'mastodon/maintenance_cli' require_relative 'mastodon/maintenance_cli'
require_relative 'mastodon/version' require_relative 'mastodon/version'
@ -62,6 +63,9 @@ module Mastodon
desc 'ip_blocks SUBCOMMAND ...ARGS', 'Manage IP blocks' desc 'ip_blocks SUBCOMMAND ...ARGS', 'Manage IP blocks'
subcommand 'ip_blocks', Mastodon::IpBlocksCLI subcommand 'ip_blocks', Mastodon::IpBlocksCLI
desc 'canonical_email_blocks SUBCOMMAND ...ARGS', 'Manage canonical e-mail blocks'
subcommand 'canonical_email_blocks', Mastodon::CanonicalEmailBlocksCLI
desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities' desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities'
subcommand 'maintenance', Mastodon::MaintenanceCLI subcommand 'maintenance', Mastodon::MaintenanceCLI

View File

@ -0,0 +1,64 @@
# frozen_string_literal: true
require 'concurrent'
require_relative '../../config/boot'
require_relative '../../config/environment'
require_relative 'cli_helper'
module Mastodon
class CanonicalEmailBlocksCLI < Thor
include CLIHelper
def self.exit_on_failure?
true
end
desc 'find EMAIL', 'Find a given e-mail address in the canonical e-mail blocks'
long_desc <<-LONG_DESC
When suspending a local user, a hash of a "canonical" version of their e-mail
address is stored to prevent them from signing up again.
This command can be used to find whether a known email address is blocked,
and if so, which account it was attached to.
LONG_DESC
def find(email)
accts = CanonicalEmailBlock.find_blocks(email).map(&:reference_account).map(&:acct).to_a
if accts.empty?
say("#{email} is not blocked", :yellow)
else
accts.each do |acct|
say(acct, :white)
end
end
end
desc 'remove EMAIL', 'Remove a canonical e-mail block'
long_desc <<-LONG_DESC
When suspending a local user, a hash of a "canonical" version of their e-mail
address is stored to prevent them from signing up again.
This command allows removing a canonical email block.
LONG_DESC
def remove(email)
blocks = CanonicalEmailBlock.find_blocks(email)
if blocks.empty?
say("#{email} is not blocked", :yellow)
else
blocks.destroy_all
say("Removed canonical email block for #{email}", :green)
end
end
private
def color(processed, failed)
if !processed.zero? && failed.zero?
:green
elsif failed.zero?
:yellow
else
:red
end
end
end
end

View File

@ -192,4 +192,36 @@ RSpec.describe Admin::AccountsController, type: :controller do
end end
end end
end end
describe 'POST #unblock_email' do
subject do
-> { post :unblock_email, params: { id: account.id } }
end
let(:current_user) { Fabricate(:user, admin: admin) }
let(:account) { Fabricate(:account, suspended: true) }
let!(:email_block) { Fabricate(:canonical_email_block, reference_account: account) }
context 'when user is admin' do
let(:admin) { true }
it 'succeeds in removing email blocks' do
is_expected.to change { CanonicalEmailBlock.where(reference_account: account).count }.from(1).to(0)
end
it 'redirects to admin account path' do
subject.call
expect(response).to redirect_to admin_account_path(account.id)
end
end
context 'when user is not admin' do
let(:admin) { false }
it 'fails to remove avatar' do
subject.call
expect(response).to have_http_status :forbidden
end
end
end
end end

View File

@ -37,7 +37,7 @@ RSpec.describe AccountPolicy do
end end
end end
permissions :unsuspend? do permissions :unsuspend?, :unblock_email? do
before do before do
alice.suspend! alice.suspend!
end end