diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index efd63e96f..505989c06 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -14,7 +14,7 @@ module Actions assign_new_registrant if params[:registrant] assign_relational_modifications assign_requested_statuses - validate_dnssec unless Rails.env.test? + ValidateDnssec.validate_dnssec(params: params, domain: domain) ::Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document]) commit @@ -114,98 +114,6 @@ module Actions end end - def prepare_resolver - dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) - dns = Dnsruby::Resolver.new({ nameserver: dns_servers }) - dns.do_validation = true - dns.do_caching = true - dns.dnssec = true - - dns - end - - def validate_dnssec - return if @params[:action] == 'rem' - - dns = prepare_resolver - subzone_records = get_dnskey_records_from_subzone(resolver: dns, hostname: @params[:domain]) - form_extension_records = extensional_dnskeys_data - - return true if form_extension_records.empty? - - validate_data(subzone_records: subzone_records, form_extension_records: form_extension_records) - end - - def make_magic(subzone_records:, form_data:) - subzone_records.any? do |subzone_data| - subzone_data[:basic] == form_data[:basic] && - subzone_data[:public_key].include?(form_data[:public_key]) - end - end - - def validate_data(subzone_records:, form_extension_records:) - flag = false - form_extension_records.each do |form_data| - flag = make_magic(subzone_records: subzone_records, form_data: form_data) - - break if flag - end - - return validation_dns_key_error unless flag - - flag - end - - def get_dnskey_records_from_subzone(resolver:, hostname:) - ds_records_answers = resolver.query(hostname, 'DNSKEY').answer - - result_container = [] - - ds_records_answers.each do |ds| - next unless ds.type == Dnsruby::Types.DNSKEY - - result_container << { - basic: { - flags: ds.flags.to_s, - algorithm: ds.algorithm.code.to_s, - protocol: ds.protocol.to_s, - }, - public_key: ds.public_key.export.gsub!(/\s+/, ''), - } - end - - result_container - rescue Dnsruby::NXDomain - domain.add_epp_error('2308', nil, nil, I18n.t(:dns_policy_violation)) - end - - def validation_dns_key_error - domain.add_epp_error('2308', nil, nil, I18n.t(:dns_policy_violation)) - end - - def extensional_dnskeys_data - dnskeys_data = @params[:dns_keys] - - return [] if dnskeys_data.nil? - - result_container = [] - - dnskeys_data.each do |ds| - next if ds[:action] == 'rem' - - result_container << { - basic: { - flags: ds[:flags].to_s, - algorithm: ds[:alg].to_s, - protocol: ds[:protocol].to_s, - }, - public_key: ds[:public_key], - } - end - - result_container - end - def assign_removable_dnskey(key) dnkey = domain.dnskeys.find_by(key.except(:action)) domain.add_epp_error(2303, nil, nil, %i[dnskeys not_found]) unless dnkey diff --git a/app/services/validate_dnssec.rb b/app/services/validate_dnssec.rb new file mode 100644 index 000000000..1cc2a9422 --- /dev/null +++ b/app/services/validate_dnssec.rb @@ -0,0 +1,97 @@ +module ValidateDnssec + include Dnsruby + + extend self + + def prepare_resolver + dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) + dns = Dnsruby::Resolver.new({ nameserver: dns_servers }) + dns.do_validation = false + dns.do_caching = false + dns.dnssec = true + + dns + end + + def validate_dnssec(params:, domain:) + return if params[:action] == 'rem' + + dns = prepare_resolver + subzone_records = get_dnskey_records_from_subzone(resolver: dns, hostname: params[:domain], domain: domain) + form_extension_records = extensional_dnskeys_data(params) + + return true if form_extension_records.empty? + + validate_data(subzone_records: subzone_records, form_extension_records: form_extension_records, domain: domain) + end + + def make_magic(subzone_records:, form_data:) + subzone_records.any? do |subzone_data| + subzone_data[:basic] == form_data[:basic] && + subzone_data[:public_key].include?(form_data[:public_key]) + end + end + + def validate_data(subzone_records:, form_extension_records:, domain:) + flag = false + form_extension_records.each do |form_data| + flag = make_magic(subzone_records: subzone_records, form_data: form_data) + + break if flag + end + + return validation_dns_key_error(domain) unless flag + + flag + end + + def get_dnskey_records_from_subzone(resolver:, hostname:, domain:) + ds_records_answers = resolver.query(hostname, 'DNSKEY').answer + + result_container = [] + + ds_records_answers.each do |ds| + next unless ds.type == Dnsruby::Types.DNSKEY + + result_container << { + basic: { + flags: ds.flags.to_s, + algorithm: ds.algorithm.code.to_s, + protocol: ds.protocol.to_s, + }, + public_key: ds.public_key.export.gsub!(/\s+/, ''), + } + end + + result_container + rescue Dnsruby::NXDomain + domain.add_epp_error('2308', nil, nil, I18n.t(:dns_policy_violation)) + end + + def validation_dns_key_error(domain) + domain.add_epp_error('2308', nil, nil, I18n.t(:dns_policy_violation)) + end + + def extensional_dnskeys_data(params) + dnskeys_data = params[:dns_keys] + + return [] if dnskeys_data.nil? + + result_container = [] + + dnskeys_data.each do |ds| + next if ds[:action] == 'rem' + + result_container << { + basic: { + flags: ds[:flags].to_s, + algorithm: ds[:alg].to_s, + protocol: ds[:protocol].to_s, + }, + public_key: ds[:public_key], + } + end + + result_container + end +end diff --git a/test/integration/api/registrant/registrant_api_verifications_test.rb b/test/integration/api/registrant/registrant_api_verifications_test.rb index 821d0dee0..8db49410f 100644 --- a/test/integration/api/registrant/registrant_api_verifications_test.rb +++ b/test/integration/api/registrant/registrant_api_verifications_test.rb @@ -15,7 +15,7 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest @domain.update!(statuses: [DomainStatus::PENDING_UPDATE], registrant_verification_asked_at: Time.zone.now - 1.day, registrant_verification_token: @token) - + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def test_fetches_registrant_change_request diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index d021b496d..aaa7e1767 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -10,6 +10,7 @@ class EppDomainUpdateBaseTest < EppTestCase @original_registrant_change_verification = Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end teardown do diff --git a/test/integration/epp/domain/update/rem_dns_test.rb b/test/integration/epp/domain/update/rem_dns_test.rb index 6e079b126..ecd660fa6 100644 --- a/test/integration/epp/domain/update/rem_dns_test.rb +++ b/test/integration/epp/domain/update/rem_dns_test.rb @@ -12,6 +12,7 @@ class EppDomainUpdateRemDnsTest < EppTestCase @original_registrant_change_verification = Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end teardown do diff --git a/test/integration/repp/v1/domains/contacts_test.rb b/test/integration/repp/v1/domains/contacts_test.rb index b9b26a745..540514853 100644 --- a/test/integration/repp/v1/domains/contacts_test.rb +++ b/test/integration/repp/v1/domains/contacts_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def test_shows_existing_domain_contacts diff --git a/test/integration/repp/v1/domains/dnssec_test.rb b/test/integration/repp/v1/domains/dnssec_test.rb index 6835e2600..79480c3b8 100644 --- a/test/integration/repp/v1/domains/dnssec_test.rb +++ b/test/integration/repp/v1/domains/dnssec_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsDnssecTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def test_shows_dnssec_keys_associated_with_domain diff --git a/test/integration/repp/v1/domains/nameservers_test.rb b/test/integration/repp/v1/domains/nameservers_test.rb index 780e889c1..4c7a1dcc5 100644 --- a/test/integration/repp/v1/domains/nameservers_test.rb +++ b/test/integration/repp/v1/domains/nameservers_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsNameserversTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def test_can_add_new_nameserver diff --git a/test/integration/repp/v1/domains/update_test.rb b/test/integration/repp/v1/domains/update_test.rb index d924fe7a3..595d094fe 100644 --- a/test/integration/repp/v1/domains/update_test.rb +++ b/test/integration/repp/v1/domains/update_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def test_updates_transfer_code_for_domain diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index 158729ae3..a5999cd36 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -14,6 +14,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase new_registrant_name: @new_registrant.name, new_registrant_email: @new_registrant.email, current_user_id: @user.id }) + Spy.on_instance_method(ValidateDnssec, :validate_dnssec).and_return(true) end def teardown