diff --git a/app/models/user.rb b/app/models/user.rb index 584120cf2e..7009ea662b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -95,7 +95,7 @@ class User < ApplicationRecord accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text } validates :invite_request, presence: true, on: :create, if: :invite_text_required? - validates :email, presence: true, email_address: true + validates :email, presence: true, email_address: true, disposable_email: true validates_with BlacklistedEmailValidator, if: -> { ENV['EMAIL_DOMAIN_LISTS_APPLY_AFTER_CONFIRMATION'] == 'true' || !confirmed? } validates_with EmailMxValidator, if: :validate_email_dns? diff --git a/app/validators/disposable_email_validator.rb b/app/validators/disposable_email_validator.rb new file mode 100644 index 0000000000..c063b704e9 --- /dev/null +++ b/app/validators/disposable_email_validator.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class DisposableEmailValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + return if value.blank? + + return unless disposable_email?(value) + + record.errors.add( + attribute, + (options[:message] || I18n.t('disposable_email_validator.invalid')) + ) + end + + private + + def disposable_email?(email) + email_address = begin + Mail::Address.new(email.downcase) + rescue + nil + end + + return false unless email_address + + disposable_email_domains.include?(email_address.domain) + end + + def disposable_email_domains + file_path = Rails.root.join('data', 'disposable_email_domains.txt') + return [] unless File.exist? file_path + + data = File.read(file_path) + JSON.parse(data) + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6cd996594d..14eb2830f9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1166,6 +1166,8 @@ en: more_details_html: For more details, see the privacy policy. username_available: Your username will become available again username_unavailable: Your username will remain unavailable + disposable_email_validator: + invalid: from disposable domain is not allowed disputes: strikes: action_taken: Action taken diff --git a/spec/validators/disposable_email_validator_spec.rb b/spec/validators/disposable_email_validator_spec.rb new file mode 100644 index 0000000000..ffca4402f2 --- /dev/null +++ b/spec/validators/disposable_email_validator_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe DisposableEmailValidator do + describe '#validate' do + it 'does not allow disposable email domains' do + user = Fabricate.build(:user, email: 'user@1994gmail.com') + expect(user).to_not be_valid + expect(user.errors.first.type).to eq I18n.t('disposable_email_validator.invalid') + end + + it 'allows valid email domain' do + user = Fabricate.build(:user, email: 'user@gmail.com') + expect(user).to be_valid + end + end +end