Merge pull request #2257 from internetee/2246-improve-email-validation-on-contact-create-and-update

added ability to validate invalid mx email by a and aaaa records
This commit is contained in:
Timo Võhmar 2021-12-29 20:42:04 +02:00 committed by GitHub
commit acc2818941
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 27 deletions

View file

@ -0,0 +1,34 @@
module Actions
module AAndAaaaEmailValidation
extend self
def call(email:, value:)
check_for_records_value(email: email, value: value)
end
private
def check_for_records_value(email:, value:)
email_domain = Mail::Address.new(email).domain
dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip)
resolve_a_and_aaaa_records(dns_servers: dns_servers, email_domain: email_domain, value: value)
end
def resolve_a_and_aaaa_records(dns_servers:, email_domain:, value:)
Resolv::DNS.open({ nameserver: dns_servers }) do |dns|
dns.timeouts = ENV['a_and_aaaa_validation_timeout'].to_i || 1
ress = nil
case value
when 'A'
ress = dns.getresources email_domain, Resolv::DNS::Resource::IN::A
when 'AAAA'
ress = dns.getresources email_domain, Resolv::DNS::Resource::IN::AAAA
end
ress.map(&:address)
end
end
end
end

View file

@ -20,14 +20,14 @@ module Actions
return if Rails.env.test? return if Rails.env.test?
[:regex, :mx].each do |m| [:regex, :mx].each do |m|
r = Actions::SimpleMailValidator.run(email: contact.email, level: m) result = Actions::SimpleMailValidator.run(email: contact.email, level: m)
unless r.success next if result
contact.add_epp_error('2005', nil, r.errors, I18n.t(:parameter_value_syntax_error))
contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error))
@error = true @error = true
return return
end end
end
true true
end end

View file

@ -24,14 +24,13 @@ module Actions
return if Rails.env.test? return if Rails.env.test?
[:regex, :mx].each do |m| [:regex, :mx].each do |m|
r = Actions::SimpleMailValidator.run(email: @new_attributes[:email], level: m) result = Actions::SimpleMailValidator.run(email: @new_attributes[:email], level: m)
next if result
unless r.success contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error))
contact.add_epp_error('2005', nil, r.errors, I18n.t(:parameter_value_syntax_error))
@error = true @error = true
return return
end end
end
true true
end end

View file

@ -51,18 +51,17 @@ module Actions
def save_result(result) def save_result(result)
if !result.success && @check_level == "mx" if !result.success && @check_level == "mx"
email_domain = Mail::Address.new(@email).domain result_validation = Actions::AAndAaaaEmailValidation.call(email: @email, value: 'A')
output_a_and_aaaa_validation_results(email: @email,
result: result_validation,
type: 'A')
result_validation = check_for_records_value(domain: email_domain, value: 'A') result_validation = Actions::AAndAaaaEmailValidation.call(email: @email, value: 'AAAA') if result_validation.empty?
logger.info "Validated A record for #{email_domain}. Validation result - #{result_validation}" output_a_and_aaaa_validation_results(email: @email,
p "Validated A record for #{email_domain}. Validation result - #{result_validation}" result: result_validation,
type: 'AAAA')
result_validation = check_for_records_value(domain: email_domain, value: 'AAAA') if result_validation.empty?
logger.info "Validated AAAA record for #{email_domain}. Validation result - #{result_validation}" if result_validation.empty?
p "Validated AAAA record for #{email_domain}. Validation result - #{result_validation}" if result_validation.empty?
result_validation.present? ? result.success = true : result.success = false result_validation.present? ? result.success = true : result.success = false
validation_eventable.validation_events.create(validation_event_attrs(result)) validation_eventable.validation_events.create(validation_event_attrs(result))
else else
validation_eventable.validation_events.create(validation_event_attrs(result)) validation_eventable.validation_events.create(validation_event_attrs(result))
@ -72,6 +71,12 @@ module Actions
true true
end end
def output_a_and_aaaa_validation_results(email:, result:, type:)
return if Rails.env.test?
logger.info "Validated #{type} record for #{email}. Validation result - #{result}"
end
def check_for_records_value(domain:, value:) def check_for_records_value(domain:, value:)
result = nil result = nil
dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip)

View file

@ -3,7 +3,37 @@ module Actions
extend self extend self
def run(email:, level:) def run(email:, level:)
Truemail.validate(email, with: level).result result = truemail_validate(email: email, level: level)
result = validate_for_a_and_aaaa_records(email) if !result && level == :mx
result
end
def truemail_validate(email:, level:)
Truemail.validate(email, with: level).result.success
end
def validate_for_a_and_aaaa_records(email)
result_validation = Actions::AAndAaaaEmailValidation.call(email: email, value: 'A')
output_a_and_aaaa_validation_results(email: email,
result: result_validation,
type: 'A')
result_validation = Actions::AAndAaaaEmailValidation.call(email: email, value: 'AAAA') if result_validation.empty?
output_a_and_aaaa_validation_results(email: email,
result: result_validation,
type: 'AAAA')
result_validation.present? ? true : false
end
def output_a_and_aaaa_validation_results(email:, result:, type:)
return if Rails.env.test?
logger.info "Validated #{type} record for #{email}. Validation result - #{result}"
end
def logger
@logger ||= Rails.logger
end end
end end
end end

View file

@ -1,6 +1,6 @@
require 'test_helper' require 'test_helper'
class DoRequestTest < ActiveSupport::TestCase class EmailCheckTest < ActiveSupport::TestCase
setup do setup do
WebMock.disable_net_connect! WebMock.disable_net_connect!
@ -16,7 +16,7 @@ class DoRequestTest < ActiveSupport::TestCase
) )
Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results) Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results)
Spy.on_instance_method(Actions::EmailCheck, :check_for_records_value).and_return([true]) Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([true])
action = Actions::EmailCheck.new(email: @contact.email, action = Actions::EmailCheck.new(email: @contact.email,
validation_eventable: @contact, validation_eventable: @contact,
@ -35,7 +35,7 @@ class DoRequestTest < ActiveSupport::TestCase
) )
Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results) Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results)
Spy.on_instance_method(Actions::EmailCheck, :check_for_records_value).and_return([]) Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([])
action = Actions::EmailCheck.new(email: @contact.email, action = Actions::EmailCheck.new(email: @contact.email,
validation_eventable: @contact, validation_eventable: @contact,
@ -54,7 +54,7 @@ class DoRequestTest < ActiveSupport::TestCase
) )
Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results) Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results)
Spy.on_instance_method(Actions::EmailCheck, :check_for_records_value).and_return([]) Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([])
action = Actions::EmailCheck.new(email: @contact.email, action = Actions::EmailCheck.new(email: @contact.email,
validation_eventable: @contact, validation_eventable: @contact,
@ -82,7 +82,7 @@ class DoRequestTest < ActiveSupport::TestCase
) )
Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results) Spy.on_instance_method(Actions::EmailCheck, :check_email).and_return(trumail_results)
Spy.on_instance_method(Actions::EmailCheck, :check_for_records_value).and_return([true]) Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([true])
action = Actions::EmailCheck.new(email: @contact.email, action = Actions::EmailCheck.new(email: @contact.email,
validation_eventable: @contact, validation_eventable: @contact,