diff --git a/app/interactions/actions/a_and_aaaa_email_validation.rb b/app/interactions/actions/a_and_aaaa_email_validation.rb new file mode 100644 index 000000000..f0ec569f4 --- /dev/null +++ b/app/interactions/actions/a_and_aaaa_email_validation.rb @@ -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 diff --git a/app/interactions/actions/contact_create.rb b/app/interactions/actions/contact_create.rb index ad5612322..d31af2e38 100644 --- a/app/interactions/actions/contact_create.rb +++ b/app/interactions/actions/contact_create.rb @@ -20,13 +20,13 @@ module Actions return if Rails.env.test? [: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 - contact.add_epp_error('2005', nil, r.errors, I18n.t(:parameter_value_syntax_error)) - @error = true - return - end + next if result + + contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error)) + @error = true + return end true diff --git a/app/interactions/actions/contact_update.rb b/app/interactions/actions/contact_update.rb index a18afed56..0cf76d116 100644 --- a/app/interactions/actions/contact_update.rb +++ b/app/interactions/actions/contact_update.rb @@ -24,13 +24,12 @@ module Actions return if Rails.env.test? [: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, r.errors, I18n.t(:parameter_value_syntax_error)) - @error = true - return - end + contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error)) + @error = true + return end true diff --git a/app/interactions/actions/email_check.rb b/app/interactions/actions/email_check.rb index 5f4616bab..b358db08d 100644 --- a/app/interactions/actions/email_check.rb +++ b/app/interactions/actions/email_check.rb @@ -51,18 +51,17 @@ module Actions def save_result(result) 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') - logger.info "Validated A record for #{email_domain}. Validation result - #{result_validation}" - p "Validated A record for #{email_domain}. Validation result - #{result_validation}" - - 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 = 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? ? result.success = true : result.success = false - validation_eventable.validation_events.create(validation_event_attrs(result)) else validation_eventable.validation_events.create(validation_event_attrs(result)) @@ -72,6 +71,12 @@ module Actions true 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:) result = nil dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) diff --git a/app/interactions/actions/simple_mail_validator.rb b/app/interactions/actions/simple_mail_validator.rb index b96c3db46..7d945e875 100644 --- a/app/interactions/actions/simple_mail_validator.rb +++ b/app/interactions/actions/simple_mail_validator.rb @@ -3,7 +3,37 @@ module Actions extend self 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 diff --git a/test/interactions/email_check_test.rb b/test/interactions/email_check_test.rb index aa5fd8dc4..4e77a5bee 100644 --- a/test/interactions/email_check_test.rb +++ b/test/interactions/email_check_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class DoRequestTest < ActiveSupport::TestCase +class EmailCheckTest < ActiveSupport::TestCase setup do 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_for_records_value).and_return([true]) + Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([true]) action = Actions::EmailCheck.new(email: @contact.email, 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_for_records_value).and_return([]) + Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([]) action = Actions::EmailCheck.new(email: @contact.email, 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_for_records_value).and_return([]) + Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([]) action = Actions::EmailCheck.new(email: @contact.email, 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_for_records_value).and_return([true]) + Spy.on_instance_method(Actions::AAndAaaaEmailValidation, :call).and_return([true]) action = Actions::EmailCheck.new(email: @contact.email, validation_eventable: @contact,