From b3af3f9f8cd5ed9c7ee06452e981b1b7734e1d89 Mon Sep 17 00:00:00 2001 From: utam0k Date: Wed, 4 Oct 2017 22:16:10 +0900 Subject: [PATCH] Implement EmailBlackList (#5109) * Implement BlacklistedEmailDomain * Use Faker::Internet.domain_name * Remove note column * Add frozen_string_literal comment * Delete unnecessary codes * Sort alphabetically * Change of wording * Rename BlacklistedEmailDomain to EmailDomainBlock --- .../admin/email_domain_blocks_controller.rb | 40 +++++++++++++ app/models/email_domain_block.rb | 17 ++++++ app/validators/blacklisted_email_validator.rb | 1 + .../_email_domain_block.html.haml | 5 ++ .../admin/email_domain_blocks/index.html.haml | 13 ++++ .../admin/email_domain_blocks/new.html.haml | 10 ++++ config/locales/en.yml | 10 ++++ config/locales/ja.yml | 10 ++++ config/navigation.rb | 1 + config/routes.rb | 1 + ...170928082043_create_email_domain_blocks.rb | 9 +++ db/schema.rb | 8 ++- .../email_domain_blocks_controller_spec.rb | 59 +++++++++++++++++++ .../email_domain_block_fabricator.rb | 3 + spec/models/email_domain_block_spec.rb | 21 +++++++ 15 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 app/controllers/admin/email_domain_blocks_controller.rb create mode 100644 app/models/email_domain_block.rb create mode 100644 app/views/admin/email_domain_blocks/_email_domain_block.html.haml create mode 100644 app/views/admin/email_domain_blocks/index.html.haml create mode 100644 app/views/admin/email_domain_blocks/new.html.haml create mode 100644 db/migrate/20170928082043_create_email_domain_blocks.rb create mode 100644 spec/controllers/admin/email_domain_blocks_controller_spec.rb create mode 100644 spec/fabricators/email_domain_block_fabricator.rb create mode 100644 spec/models/email_domain_block_spec.rb diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb new file mode 100644 index 00000000000..09275d5dc82 --- /dev/null +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Admin + class EmailDomainBlocksController < BaseController + before_action :set_email_domain_block, only: [:show, :destroy] + + def index + @email_domain_blocks = EmailDomainBlock.page(params[:page]) + end + + def new + @email_domain_block = EmailDomainBlock.new + end + + def create + @email_domain_block = EmailDomainBlock.new(resource_params) + + if @email_domain_block.save + redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg') + else + render :new + end + end + + def destroy + @email_domain_block.destroy + redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg') + end + + private + + def set_email_domain_block + @email_domain_block = EmailDomainBlock.find(params[:id]) + end + + def resource_params + params.require(:email_domain_block).permit(:domain) + end + end +end diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb new file mode 100644 index 00000000000..839038bea6d --- /dev/null +++ b/app/models/email_domain_block.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: email_domain_blocks +# +# id :integer not null, primary key +# domain :string not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class EmailDomainBlock < ApplicationRecord + def self.block?(email) + domain = email.gsub(/.+@([^.]+)/, '\1') + where(domain: domain).exists? + end +end diff --git a/app/validators/blacklisted_email_validator.rb b/app/validators/blacklisted_email_validator.rb index 0ba79694b34..3f203f49a6e 100644 --- a/app/validators/blacklisted_email_validator.rb +++ b/app/validators/blacklisted_email_validator.rb @@ -12,6 +12,7 @@ class BlacklistedEmailValidator < ActiveModel::Validator end def on_blacklist?(value) + return true if EmailDomainBlock.block?(value) return false if Rails.configuration.x.email_domains_blacklist.blank? domains = Rails.configuration.x.email_domains_blacklist.gsub('.', '\.') diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml new file mode 100644 index 00000000000..61cff93955f --- /dev/null +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -0,0 +1,5 @@ +%tr + %td.domain + %samp= email_domain_block.domain + %td + = table_link_to 'trash', t('admin.email_domain_blocks.delete'), admin_email_domain_block_path(email_domain_block), method: :delete diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml new file mode 100644 index 00000000000..fbdb3b80bde --- /dev/null +++ b/app/views/admin/email_domain_blocks/index.html.haml @@ -0,0 +1,13 @@ +- content_for :page_title do + = t('admin.email_domain_blocks.title') + +%table.table + %thead + %tr + %th= t('admin.email_domain_blocks.domain') + %th + %tbody + = render @email_domain_blocks + += paginate @email_domain_blocks += link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button' diff --git a/app/views/admin/email_domain_blocks/new.html.haml b/app/views/admin/email_domain_blocks/new.html.haml new file mode 100644 index 00000000000..bcae867d957 --- /dev/null +++ b/app/views/admin/email_domain_blocks/new.html.haml @@ -0,0 +1,10 @@ +- content_for :page_title do + = t('.title') + += simple_form_for @email_domain_block, url: admin_email_domain_blocks_path do |f| + = render 'shared/error_messages', object: @email_domain_block + + = f.input :domain, placeholder: t('admin.email_domain_blocks.domain') + + .actions + = f.button :button, t('.create'), type: :submit diff --git a/config/locales/en.yml b/config/locales/en.yml index 4a6df8cb280..5d9557535c1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -152,6 +152,16 @@ en: undo: Undo title: Domain Blocks undo: Undo + email_domain_blocks: + add_new: Add new + created_msg: Email domain block successfully created + delete: Delete + destroyed_msg: Email domain block successfully deleted + domain: Domain + new: + create: Create block + title: New email domain block + title: Email Domain Block instances: account_count: Known accounts domain_name: Domain diff --git a/config/locales/ja.yml b/config/locales/ja.yml index d637a99ea8c..3d6f2fd0bca 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -152,6 +152,16 @@ ja: undo: 元に戻す title: ドメインブロック undo: 元に戻す + email_domain_blocks: + add_new: 新規追加 + created_msg: 処理を完了しました + delete: 消去 + destroyed_msg: 消去しました + domain: ドメイン + new: + create: ブロックを作成 + title: 新規メールドメインブロック + title: メールドメインブロック instances: account_count: 既知のアカウント数 domain_name: ドメイン名 diff --git a/config/navigation.rb b/config/navigation.rb index 215d843b91d..50bfbd48011 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -26,6 +26,7 @@ SimpleNavigation::Configuration.run do |navigation| admin.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url, highlights_on: %r{/admin/instances} admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url admin.item :domain_blocks, safe_join([fa_icon('lock fw'), t('admin.domain_blocks.title')]), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks} + admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks} admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' } admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' } admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url diff --git a/config/routes.rb b/config/routes.rb index 8e80e151035..959afc23f86 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -108,6 +108,7 @@ Rails.application.routes.draw do namespace :admin do resources :subscriptions, only: [:index] resources :domain_blocks, only: [:index, :new, :create, :show, :destroy] + resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resource :settings, only: [:edit, :update] resources :instances, only: [:index] do diff --git a/db/migrate/20170928082043_create_email_domain_blocks.rb b/db/migrate/20170928082043_create_email_domain_blocks.rb new file mode 100644 index 00000000000..1f0fb75875a --- /dev/null +++ b/db/migrate/20170928082043_create_email_domain_blocks.rb @@ -0,0 +1,9 @@ +class CreateEmailDomainBlocks < ActiveRecord::Migration[5.1] + def change + create_table :email_domain_blocks do |t| + t.string :domain, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 00cc24baefe..337678c67fc 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: 20170927215609) do +ActiveRecord::Schema.define(version: 20170928082043) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -110,6 +110,12 @@ ActiveRecord::Schema.define(version: 20170927215609) do t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true end + create_table "email_domain_blocks", force: :cascade do |t| + t.string "domain", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "favourites", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb new file mode 100644 index 00000000000..295de9073ac --- /dev/null +++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Admin::EmailDomainBlocksController, type: :controller do + render_views + + before do + sign_in Fabricate(:user, admin: true), scope: :user + end + + describe 'GET #index' do + around do |example| + default_per_page = EmailDomainBlock.default_per_page + EmailDomainBlock.paginates_per 1 + example.run + EmailDomainBlock.paginates_per default_per_page + end + + it 'renders email blacks' do + 2.times { Fabricate(:email_domain_block) } + + get :index, params: { page: 2 } + + assigned = assigns(:email_domain_blocks) + expect(assigned.count).to eq 1 + expect(assigned.klass).to be EmailDomainBlock + expect(response).to have_http_status(:success) + end + end + + describe 'GET #new' do + it 'assigns a new email black' do + get :new + + expect(assigns(:email_domain_block)).to be_instance_of(EmailDomainBlock) + expect(response).to have_http_status(:success) + end + end + + describe 'POST #create' do + it 'blocks the domain when succeeded to save' do + post :create, params: { email_domain_block: { domain: 'example.com'} } + + expect(flash[:notice]).to eq I18n.t('admin.email_domain_blocks.created_msg') + expect(response).to redirect_to(admin_email_domain_blocks_path) + end + end + + describe 'DELETE #destroy' do + it 'unblocks the domain' do + email_domain_block = Fabricate(:email_domain_block) + delete :destroy, params: { id: email_domain_block.id } + + expect(flash[:notice]).to eq I18n.t('admin.email_domain_blocks.destroyed_msg') + expect(response).to redirect_to(admin_email_domain_blocks_path) + end + end +end diff --git a/spec/fabricators/email_domain_block_fabricator.rb b/spec/fabricators/email_domain_block_fabricator.rb new file mode 100644 index 00000000000..d18af6433c8 --- /dev/null +++ b/spec/fabricators/email_domain_block_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:email_domain_block) do + domain { sequence(:domain) { |i| "#{i}#{Faker::Internet.domain_name}" } } +end diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb new file mode 100644 index 00000000000..5f5d189d9d3 --- /dev/null +++ b/spec/models/email_domain_block_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe EmailDomainBlock, type: :model do + describe 'validations' do + it 'has a valid fabricator' do + email_domain_block = Fabricate.build(:email_domain_block) + expect(email_domain_block).to be_valid + end + end + + describe 'block?' do + it 'returns true if the domain is registed' do + Fabricate(:email_domain_block, domain: 'example.com') + expect(EmailDomainBlock.block?('nyarn@example.com')).to eq true + end + it 'returns true if the domain is not registed' do + Fabricate(:email_domain_block, domain: 'domain') + expect(EmailDomainBlock.block?('example')).to eq false + end + end +end