Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03db72a6f4 | ||
|
|
1df28973ed | ||
|
|
4baf8d4409 | ||
|
|
5fff10325e | ||
|
|
4303dc32fc | ||
|
|
86c8f1ec67 | ||
|
|
685a1dda48 | ||
|
|
0cb5eb8a5f |
@@ -20,7 +20,7 @@ gem 'rest-client'
|
||||
gem 'telephone_number'
|
||||
gem 'time_diff'
|
||||
gem 'tzinfo-data'
|
||||
gem 'valid_email2'
|
||||
gem 'valid_email2', '~> 7.0'
|
||||
gem 'email-provider-info'
|
||||
gem 'gemoji'
|
||||
# compress javascript config.assets.js_compressor
|
||||
|
||||
+11
-10
@@ -167,8 +167,8 @@ GEM
|
||||
statsd-ruby (~> 1.1)
|
||||
base64 (0.3.0)
|
||||
bcrypt (3.1.22)
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.2.2)
|
||||
benchmark (0.5.0)
|
||||
bigdecimal (3.3.1)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.16.0)
|
||||
msgpack (~> 1.2)
|
||||
@@ -190,7 +190,7 @@ GEM
|
||||
climate_control (1.2.0)
|
||||
coderay (1.1.3)
|
||||
commonmarker (0.23.10)
|
||||
concurrent-ruby (1.3.5)
|
||||
concurrent-ruby (1.3.6)
|
||||
connection_pool (2.5.5)
|
||||
crack (1.0.0)
|
||||
bigdecimal
|
||||
@@ -451,7 +451,7 @@ GEM
|
||||
mini_mime (>= 1.0.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.7)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
@@ -545,7 +545,8 @@ GEM
|
||||
loofah (2.23.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
mail (2.9.0)
|
||||
logger
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
@@ -562,7 +563,7 @@ GEM
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.9)
|
||||
minitest (5.25.5)
|
||||
minitest (5.27.0)
|
||||
mock_redis (0.36.0)
|
||||
ruby2_keywords
|
||||
msgpack (1.8.0)
|
||||
@@ -577,7 +578,7 @@ GEM
|
||||
uri (>= 0.11.1)
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.24)
|
||||
net-imap (0.6.4)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
@@ -991,8 +992,8 @@ GEM
|
||||
uri (1.1.1)
|
||||
uri-idna (0.3.1)
|
||||
uri_template (0.7.0)
|
||||
valid_email2 (5.2.6)
|
||||
activemodel (>= 3.2)
|
||||
valid_email2 (7.0.15)
|
||||
activemodel (>= 6.0)
|
||||
mail (~> 2.5)
|
||||
version_gem (1.1.4)
|
||||
vite_rails (3.0.17)
|
||||
@@ -1181,7 +1182,7 @@ DEPENDENCIES
|
||||
twitty (~> 0.1.5)
|
||||
tzinfo-data
|
||||
uglifier
|
||||
valid_email2
|
||||
valid_email2 (~> 7.0)
|
||||
vite_rails
|
||||
web-console (>= 4.2.1)
|
||||
web-push (>= 3.0.1)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
class AccountBuilder
|
||||
include CustomExceptions::Account
|
||||
pattr_initialize [:account_name, :email!, :confirmed, :user, :user_full_name, :user_password, :super_admin, :locale]
|
||||
pattr_initialize [:account_name, :email!, :confirmed, :user, :user_full_name, :user_password, :super_admin, :locale,
|
||||
:allow_free_email_provider]
|
||||
|
||||
def perform
|
||||
if @user.nil?
|
||||
@@ -32,7 +33,7 @@ class AccountBuilder
|
||||
end
|
||||
|
||||
def validate_email
|
||||
Account::SignUpEmailValidationService.new(@email).perform
|
||||
Account::SignUpEmailValidationService.new(@email, allow_free_email_provider: @allow_free_email_provider).perform
|
||||
end
|
||||
|
||||
def validate_user
|
||||
|
||||
@@ -11,7 +11,8 @@ class Installation::OnboardingController < ApplicationController
|
||||
email: onboarding_params.dig(:user, :email),
|
||||
user_password: params.dig(:user, :password),
|
||||
super_admin: true,
|
||||
confirmed: true
|
||||
confirmed: true,
|
||||
allow_free_email_provider: true
|
||||
).perform
|
||||
rescue StandardError => e
|
||||
redirect_to '/', flash: { error: e.message } and return
|
||||
|
||||
@@ -4,8 +4,9 @@ class Account::SignUpEmailValidationService
|
||||
include CustomExceptions::Account
|
||||
attr_reader :email
|
||||
|
||||
def initialize(email)
|
||||
def initialize(email, allow_free_email_provider: false)
|
||||
@email = email
|
||||
@allow_free_email_provider = allow_free_email_provider
|
||||
end
|
||||
|
||||
def perform
|
||||
@@ -17,6 +18,8 @@ class Account::SignUpEmailValidationService
|
||||
|
||||
raise InvalidEmail.new({ valid: true, disposable: true }) if address.disposable?
|
||||
|
||||
raise InvalidEmail.new({ free_email_provider: true }) if !@allow_free_email_provider && address.deny_listed?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,7 @@ en:
|
||||
signup:
|
||||
disposable_email: We do not allow disposable emails
|
||||
blocked_domain: This domain is not allowed. If you believe this is a mistake, please contact support.
|
||||
free_email_provider: Please sign up with your work email address.
|
||||
invalid_email: You have entered an invalid email
|
||||
email_already_exists: 'You have already signed up for an account with %{email}'
|
||||
invalid_params: 'Invalid, please check the signup paramters and try again'
|
||||
|
||||
@@ -7,6 +7,8 @@ module CustomExceptions::Account
|
||||
I18n.t 'errors.signup.blocked_domain'
|
||||
elsif @data[:disposable]
|
||||
I18n.t 'errors.signup.disposable_email'
|
||||
elsif @data[:free_email_provider]
|
||||
I18n.t 'errors.signup.free_email_provider'
|
||||
elsif !@data[:valid]
|
||||
I18n.t 'errors.signup.invalid_email'
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ RSpec.describe AccountBuilder do
|
||||
|
||||
# Mock the email validation service
|
||||
before do
|
||||
allow(Account::SignUpEmailValidationService).to receive(:new).with(email).and_return(validation_service)
|
||||
allow(Account::SignUpEmailValidationService).to receive(:new).with(email, allow_free_email_provider: anything).and_return(validation_service)
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
|
||||
@@ -5,7 +5,8 @@ require 'rails_helper'
|
||||
RSpec.describe Account::SignUpEmailValidationService, type: :service do
|
||||
let(:service) { described_class.new(email) }
|
||||
let(:blocked_domains) { "gmail.com\noutlook.com" }
|
||||
let(:valid_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false) }
|
||||
let(:valid_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false, deny_listed?: false) }
|
||||
let(:free_provider_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: false, deny_listed?: true) }
|
||||
let(:disposable_email_address) { instance_double(ValidEmail2::Address, valid?: true, disposable?: true) }
|
||||
let(:invalid_email_address) { instance_double(ValidEmail2::Address, valid?: false) }
|
||||
|
||||
@@ -62,13 +63,77 @@ RSpec.describe Account::SignUpEmailValidationService, type: :service do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email is valid business email' do
|
||||
context 'when email is from a free provider' do
|
||||
let(:email) { 'test@example.com' }
|
||||
|
||||
it 'raises InvalidEmail with free email provider message' do
|
||||
allow(ValidEmail2::Address).to receive(:new).with(email).and_return(free_provider_email_address)
|
||||
expect { service.perform }.to raise_error do |error|
|
||||
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
|
||||
expect(error.message).to eq(I18n.t('errors.signup.free_email_provider'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email is valid business email' do
|
||||
let(:email) { 'test@chatwoot.com' }
|
||||
|
||||
it 'returns true' do
|
||||
allow(ValidEmail2::Address).to receive(:new).with(email).and_return(valid_email_address)
|
||||
expect(service.perform).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Exercises the real ValidEmail2 + config/deny_listed_email_domains.yml
|
||||
# integration without stubbing ValidEmail2::Address. Guards against the YAML
|
||||
# file going missing, the path drifting, or upstream API renames.
|
||||
describe '#perform with the real deny list' do
|
||||
before do
|
||||
allow(GlobalConfigService).to receive(:load).with('BLOCKED_EMAIL_DOMAINS', '').and_return('')
|
||||
# ValidEmail2::Dns is instantiated lazily inside ValidEmail2::Address per call,
|
||||
# so we cannot capture the instance to stub it. Stubbing the class-level constructor
|
||||
# would also break ValidEmail2's internal caching. Returning empty MX/A records keeps
|
||||
# disposable? and valid_mx? offline and deterministic in CI.
|
||||
allow_any_instance_of(ValidEmail2::Dns).to receive_messages(mx_servers: [], a_servers: []) # rubocop:disable RSpec/AnyInstance
|
||||
end
|
||||
|
||||
%w[user@gmail.com user@outlook.com user@hotmail.com user@yahoo.com user@protonmail.com user@icloud.com].each do |email|
|
||||
it "rejects #{email} with the free_email_provider message" do
|
||||
expect { described_class.new(email).perform }.to raise_error do |error|
|
||||
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
|
||||
expect(error.message).to eq(I18n.t('errors.signup.free_email_provider'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects a subdomain of a free provider via ValidEmail2 depth walking' do
|
||||
expect { described_class.new('user@mail.gmail.com').perform }.to raise_error do |error|
|
||||
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
|
||||
expect(error.message).to eq(I18n.t('errors.signup.free_email_provider'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows a real business domain' do
|
||||
expect(described_class.new('hello@chatwoot.com').perform).to be(true)
|
||||
end
|
||||
|
||||
it 'still rejects a disposable email with the disposable message, not the free_email message' do
|
||||
expect { described_class.new('user@mailinator.com').perform }.to raise_error do |error|
|
||||
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
|
||||
expect(error.message).to eq(I18n.t('errors.signup.disposable_email'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows free email providers when allow_free_email_provider is true' do
|
||||
expect(described_class.new('admin@gmail.com', allow_free_email_provider: true).perform).to be(true)
|
||||
end
|
||||
|
||||
it 'still rejects disposable emails even when allow_free_email_provider is true' do
|
||||
expect { described_class.new('user@mailinator.com', allow_free_email_provider: true).perform }.to raise_error do |error|
|
||||
expect(error.class.name).to eq('CustomExceptions::Account::InvalidEmail')
|
||||
expect(error.message).to eq(I18n.t('errors.signup.disposable_email'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user