forked from treehouse/mastodon
Add option to include resolved DNS records when blacklisting e-mail domains in admin UI (#13254)
* Add shortcuts to blacklist a user's e-mail domain in admin UI * Add option to blacklist resolved MX and IP records for e-mail domainssignup-info-prompt
parent
f556f79b77
commit
bea0bb39d6
|
@ -6,12 +6,12 @@ module Admin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :email_domain_block, :index?
|
authorize :email_domain_block, :index?
|
||||||
@email_domain_blocks = EmailDomainBlock.page(params[:page])
|
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :email_domain_block, :create?
|
authorize :email_domain_block, :create?
|
||||||
@email_domain_block = EmailDomainBlock.new
|
@email_domain_block = EmailDomainBlock.new(domain: params[:_domain])
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -21,6 +21,28 @@ module Admin
|
||||||
|
|
||||||
if @email_domain_block.save
|
if @email_domain_block.save
|
||||||
log_action :create, @email_domain_block
|
log_action :create, @email_domain_block
|
||||||
|
|
||||||
|
if @email_domain_block.with_dns_records?
|
||||||
|
hostnames = []
|
||||||
|
ips = []
|
||||||
|
|
||||||
|
Resolv::DNS.open do |dns|
|
||||||
|
dns.timeouts = 1
|
||||||
|
|
||||||
|
hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
|
||||||
|
|
||||||
|
([@email_domain_block.domain] + hostnames).uniq.each do |hostname|
|
||||||
|
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s })
|
||||||
|
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::AAAA).to_a.map { |e| e.address.to_s })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(hostnames + ips).each do |hostname|
|
||||||
|
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: @email_domain_block)
|
||||||
|
log_action :create, another_email_domain_block if another_email_domain_block.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
||||||
else
|
else
|
||||||
render :new
|
render :new
|
||||||
|
@ -41,7 +63,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:email_domain_block).permit(:domain)
|
params.require(:email_domain_block).permit(:domain, :with_dns_records)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,13 +7,27 @@
|
||||||
# domain :string default(""), not null
|
# domain :string default(""), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# parent_id :bigint(8)
|
||||||
#
|
#
|
||||||
|
|
||||||
class EmailDomainBlock < ApplicationRecord
|
class EmailDomainBlock < ApplicationRecord
|
||||||
include DomainNormalizable
|
include DomainNormalizable
|
||||||
|
|
||||||
|
belongs_to :parent, class_name: 'EmailDomainBlock', optional: true
|
||||||
|
has_many :children, class_name: 'EmailDomainBlock', foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
|
||||||
|
|
||||||
validates :domain, presence: true, uniqueness: true, domain: true
|
validates :domain, presence: true, uniqueness: true, domain: true
|
||||||
|
|
||||||
|
def with_dns_records=(val)
|
||||||
|
@with_dns_records = ActiveModel::Type::Boolean.new.cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_dns_records?
|
||||||
|
@with_dns_records
|
||||||
|
end
|
||||||
|
|
||||||
|
alias with_dns_records with_dns_records?
|
||||||
|
|
||||||
def self.block?(email)
|
def self.block?(email)
|
||||||
_, domain = email.split('@', 2)
|
_, domain = email.split('@', 2)
|
||||||
|
|
||||||
|
|
|
@ -96,10 +96,17 @@
|
||||||
= table_link_to 'angle-double-down', t('admin.accounts.demote'), demote_admin_account_role_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:demote, @account.user)
|
= table_link_to 'angle-double-down', t('admin.accounts.demote'), demote_admin_account_role_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:demote, @account.user)
|
||||||
|
|
||||||
%tr
|
%tr
|
||||||
%th= t('admin.accounts.email')
|
%th{ rowspan: can?(:create, :email_domain_block) ? 3 : 2 }= t('admin.accounts.email')
|
||||||
%td= @account.user_email
|
%td{ rowspan: can?(:create, :email_domain_block) ? 3 : 2 }= @account.user_email
|
||||||
%td= table_link_to 'edit', t('admin.accounts.change_email.label'), admin_account_change_email_path(@account.id) if can?(:change_email, @account.user)
|
%td= table_link_to 'edit', t('admin.accounts.change_email.label'), admin_account_change_email_path(@account.id) if can?(:change_email, @account.user)
|
||||||
|
|
||||||
|
%tr
|
||||||
|
%td= table_link_to 'search', t('admin.accounts.search_same_email_domain'), admin_accounts_path(email: "%@#{@account.user_email.split('@').last}")
|
||||||
|
|
||||||
|
- if can?(:create, :email_domain_block)
|
||||||
|
%tr
|
||||||
|
%td= table_link_to 'ban', t('admin.accounts.add_email_domain_block'), new_admin_email_domain_block_path(_domain: @account.user_email.split('@').last)
|
||||||
|
|
||||||
- if @account.user_unconfirmed_email.present?
|
- if @account.user_unconfirmed_email.present?
|
||||||
%tr
|
%tr
|
||||||
%th= t('admin.accounts.unconfirmed_email')
|
%th= t('admin.accounts.unconfirmed_email')
|
||||||
|
@ -204,7 +211,7 @@
|
||||||
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@account.id, type: 'suspend'), class: 'button button--destructive' if can?(:suspend, @account)
|
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@account.id, type: 'suspend'), class: 'button button--destructive' if can?(:suspend, @account)
|
||||||
|
|
||||||
- unless @account.local?
|
- unless @account.local?
|
||||||
- if DomainBlock.where(domain: @account.domain).exists?
|
- if DomainBlock.rule_for(@account.domain)
|
||||||
= link_to t('admin.domain_blocks.view'), admin_instance_path(@account.domain), class: 'button'
|
= link_to t('admin.domain_blocks.view'), admin_instance_path(@account.domain), class: 'button'
|
||||||
- else
|
- else
|
||||||
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive'
|
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive'
|
||||||
|
|
|
@ -3,3 +3,13 @@
|
||||||
%samp= email_domain_block.domain
|
%samp= email_domain_block.domain
|
||||||
%td
|
%td
|
||||||
= table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(email_domain_block), method: :delete
|
= table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(email_domain_block), method: :delete
|
||||||
|
|
||||||
|
- email_domain_block.children.each do |child_email_domain_block|
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%samp= child_email_domain_block.domain
|
||||||
|
%span.muted-hint
|
||||||
|
= surround '(', ')' do
|
||||||
|
= t('admin.email_domain_blocks.from_html', domain: content_tag(:samp, email_domain_block.domain))
|
||||||
|
%td
|
||||||
|
= table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(child_email_domain_block), method: :delete
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
= render 'shared/error_messages', object: @email_domain_block
|
= render 'shared/error_messages', object: @email_domain_block
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :domain, wrapper: :with_label, label: t('admin.email_domain_blocks.domain')
|
= f.input :domain, wrapper: :with_block_label, label: t('admin.email_domain_blocks.domain')
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :with_dns_records, as: :boolean, wrapper: :with_label
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= f.button :button, t('.create'), type: :submit
|
= f.button :button, t('.create'), type: :submit
|
||||||
|
|
|
@ -92,6 +92,7 @@ en:
|
||||||
delete: Delete
|
delete: Delete
|
||||||
destroyed_msg: Moderation note successfully destroyed!
|
destroyed_msg: Moderation note successfully destroyed!
|
||||||
accounts:
|
accounts:
|
||||||
|
add_email_domain_block: Blacklist e-mail domain
|
||||||
approve: Approve
|
approve: Approve
|
||||||
approve_all: Approve all
|
approve_all: Approve all
|
||||||
are_you_sure: Are you sure?
|
are_you_sure: Are you sure?
|
||||||
|
@ -172,6 +173,7 @@ en:
|
||||||
staff: Staff
|
staff: Staff
|
||||||
user: User
|
user: User
|
||||||
search: Search
|
search: Search
|
||||||
|
search_same_email_domain: Other users with the same e-mail domain
|
||||||
search_same_ip: Other users with the same IP
|
search_same_ip: Other users with the same IP
|
||||||
shared_inbox_url: Shared inbox URL
|
shared_inbox_url: Shared inbox URL
|
||||||
show:
|
show:
|
||||||
|
@ -358,6 +360,7 @@ en:
|
||||||
destroyed_msg: Successfully deleted e-mail domain from blacklist
|
destroyed_msg: Successfully deleted e-mail domain from blacklist
|
||||||
domain: Domain
|
domain: Domain
|
||||||
empty: No e-mail domains currently blacklisted.
|
empty: No e-mail domains currently blacklisted.
|
||||||
|
from_html: from %{domain}
|
||||||
new:
|
new:
|
||||||
create: Add domain
|
create: Add domain
|
||||||
title: New e-mail blacklist entry
|
title: New e-mail blacklist entry
|
||||||
|
|
|
@ -54,6 +54,9 @@ en:
|
||||||
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
|
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
|
||||||
domain_allow:
|
domain_allow:
|
||||||
domain: This domain will be able to fetch data from this server and incoming data from it will be processed and stored
|
domain: This domain will be able to fetch data from this server and incoming data from it will be processed and stored
|
||||||
|
email_domain_block:
|
||||||
|
domain: This can be the domain name that shows up in the e-mail address, the MX record that domain resolves to, or IP of the server that MX record resolves to. Those will be checked upon user sign-up and the sign-up will be rejected.
|
||||||
|
with_dns_records: An attempt to resolve the given domain's DNS records will be made and the results will also be blacklisted
|
||||||
featured_tag:
|
featured_tag:
|
||||||
name: 'You might want to use one of these:'
|
name: 'You might want to use one of these:'
|
||||||
form_challenge:
|
form_challenge:
|
||||||
|
@ -152,6 +155,8 @@ en:
|
||||||
username: Username
|
username: Username
|
||||||
username_or_email: Username or Email
|
username_or_email: Username or Email
|
||||||
whole_word: Whole word
|
whole_word: Whole word
|
||||||
|
email_domain_block:
|
||||||
|
with_dns_records: Include MX records and IPs of the domain
|
||||||
featured_tag:
|
featured_tag:
|
||||||
name: Hashtag
|
name: Hashtag
|
||||||
interactions:
|
interactions:
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddParentIdToEmailDomainBlocks < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_reference :email_domain_blocks, :parent, null: true, default: nil, foreign_key: { on_delete: :cascade, to_table: :email_domain_blocks }, index: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2020_03_12_144258) do
|
ActiveRecord::Schema.define(version: 2020_03_12_185443) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -336,6 +336,7 @@ ActiveRecord::Schema.define(version: 2020_03_12_144258) do
|
||||||
t.string "domain", default: "", null: false
|
t.string "domain", default: "", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.bigint "parent_id"
|
||||||
t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true
|
t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -869,6 +870,7 @@ ActiveRecord::Schema.define(version: 2020_03_12_144258) do
|
||||||
add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade
|
add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade
|
||||||
add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade
|
add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade
|
||||||
add_foreign_key "custom_filters", "accounts", on_delete: :cascade
|
add_foreign_key "custom_filters", "accounts", on_delete: :cascade
|
||||||
|
add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade
|
||||||
add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade
|
add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade
|
||||||
add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade
|
add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade
|
||||||
add_foreign_key "featured_tags", "accounts", on_delete: :cascade
|
add_foreign_key "featured_tags", "accounts", on_delete: :cascade
|
||||||
|
|
Loading…
Reference in New Issue