mirror of
https://github.com/internetee/registry.git
synced 2025-06-06 20:55:44 +02:00
Merge branch '1884-unable-to-apply-registry-lock' of https://github.com/internetee/registry into 1884-unable-to-apply-registry-lock
This commit is contained in:
commit
b7149387d9
29 changed files with 1591 additions and 113 deletions
|
@ -1,3 +1,6 @@
|
|||
31.03.2021
|
||||
* Implemented child to parent syncronisation (csync) for cdnskeys [#658](https://github.com/internetee/registry/issues/658)
|
||||
|
||||
29.03.2021
|
||||
* Full EPP functionality to REPP API [#1756](https://github.com/internetee/registry/issues/1756)
|
||||
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -5,6 +5,8 @@ gem 'active_interaction', '~> 3.8'
|
|||
gem 'apipie-rails', '~> 0.5.18'
|
||||
gem 'bootsnap', '>= 1.1.0', require: false
|
||||
gem 'iso8601', '0.12.1' # for dates and times
|
||||
gem 'mime-types-data'
|
||||
gem 'mimemagic', '0.3.10'
|
||||
gem 'rails', '~> 6.0'
|
||||
gem 'rest-client'
|
||||
gem 'uglifier'
|
||||
|
@ -39,6 +41,7 @@ gem 'devise', '~> 4.7'
|
|||
|
||||
# registry specfic
|
||||
gem 'data_migrate', '~> 6.1'
|
||||
gem 'dnsruby', '~> 1.61'
|
||||
gem 'isikukood' # for EE-id validation
|
||||
gem 'simpleidn', '0.1.1' # For punycode
|
||||
gem 'money-rails'
|
||||
|
|
116
Gemfile.lock
116
Gemfile.lock
|
@ -76,60 +76,60 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
actioncable (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
activejob (= 6.0.3.5)
|
||||
activerecord (= 6.0.3.5)
|
||||
activestorage (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
actionmailbox (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
activejob (= 6.0.3.6)
|
||||
activerecord (= 6.0.3.6)
|
||||
activestorage (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
actionview (= 6.0.3.5)
|
||||
activejob (= 6.0.3.5)
|
||||
actionmailer (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
actionview (= 6.0.3.6)
|
||||
activejob (= 6.0.3.6)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.0.3.5)
|
||||
actionview (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
actionpack (6.0.3.6)
|
||||
actionview (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
activerecord (= 6.0.3.5)
|
||||
activestorage (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
actiontext (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
activerecord (= 6.0.3.6)
|
||||
activestorage (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
actionview (6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
active_interaction (3.8.3)
|
||||
activemodel (>= 4, < 7)
|
||||
activejob (6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
activejob (6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
activerecord (6.0.3.5)
|
||||
activemodel (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
activemodel (6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
activerecord (6.0.3.6)
|
||||
activemodel (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
activerecord-import (1.0.8)
|
||||
activerecord (>= 3.2)
|
||||
activestorage (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
activejob (= 6.0.3.5)
|
||||
activerecord (= 6.0.3.5)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (6.0.3.5)
|
||||
activestorage (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
activejob (= 6.0.3.6)
|
||||
activerecord (= 6.0.3.6)
|
||||
marcel (~> 1.0.0)
|
||||
activesupport (6.0.3.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
@ -215,6 +215,8 @@ GEM
|
|||
railties (>= 4.1.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
dnsruby (1.61.5)
|
||||
simpleidn (~> 0.1)
|
||||
docile (1.3.5)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
|
@ -277,14 +279,15 @@ GEM
|
|||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (0.3.3)
|
||||
mimemagic (~> 0.3.2)
|
||||
marcel (1.0.0)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2021.0225)
|
||||
mimemagic (0.3.5)
|
||||
mini_mime (1.0.2)
|
||||
mimemagic (0.3.10)
|
||||
nokogiri (~> 1)
|
||||
rake
|
||||
mini_mime (1.0.3)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.4)
|
||||
monetize (1.9.4)
|
||||
|
@ -350,29 +353,29 @@ GEM
|
|||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.0.3.5)
|
||||
actioncable (= 6.0.3.5)
|
||||
actionmailbox (= 6.0.3.5)
|
||||
actionmailer (= 6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
actiontext (= 6.0.3.5)
|
||||
actionview (= 6.0.3.5)
|
||||
activejob (= 6.0.3.5)
|
||||
activemodel (= 6.0.3.5)
|
||||
activerecord (= 6.0.3.5)
|
||||
activestorage (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
rails (6.0.3.6)
|
||||
actioncable (= 6.0.3.6)
|
||||
actionmailbox (= 6.0.3.6)
|
||||
actionmailer (= 6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
actiontext (= 6.0.3.6)
|
||||
actionview (= 6.0.3.6)
|
||||
activejob (= 6.0.3.6)
|
||||
activemodel (= 6.0.3.6)
|
||||
activerecord (= 6.0.3.6)
|
||||
activestorage (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 6.0.3.5)
|
||||
railties (= 6.0.3.6)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.3.0)
|
||||
loofah (~> 2.3)
|
||||
railties (6.0.3.5)
|
||||
actionpack (= 6.0.3.5)
|
||||
activesupport (= 6.0.3.5)
|
||||
railties (6.0.3.6)
|
||||
actionpack (= 6.0.3.6)
|
||||
activesupport (= 6.0.3.6)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.20.3, < 2.0)
|
||||
|
@ -519,6 +522,7 @@ DEPENDENCIES
|
|||
devise (~> 4.7)
|
||||
digidoc_client!
|
||||
directo!
|
||||
dnsruby (~> 1.61)
|
||||
domain_name
|
||||
e_invoice!
|
||||
epp!
|
||||
|
@ -531,6 +535,8 @@ DEPENDENCIES
|
|||
jquery-ui-rails (= 5.0.5)
|
||||
kaminari
|
||||
lhv!
|
||||
mime-types-data
|
||||
mimemagic (= 0.3.10)
|
||||
minitest (~> 5.14)
|
||||
money-rails
|
||||
nokogiri (~> 1.10.0)
|
||||
|
|
124
app/jobs/csync_job.rb
Normal file
124
app/jobs/csync_job.rb
Normal file
|
@ -0,0 +1,124 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CsyncJob < Que::Job
|
||||
def run(generate: false)
|
||||
@store = {}
|
||||
@input_store = { secure: {}, insecure: {} }
|
||||
@results = {}
|
||||
@logger = Rails.env.test? ? Rails.logger : Logger.new(STDOUT)
|
||||
generate ? generate_scanner_input : process_scanner_results
|
||||
|
||||
@logger.info 'CsyncJob: Finished.'
|
||||
end
|
||||
|
||||
def qualified_for_monitoring?(domain, data)
|
||||
result_types = data[:ns].map { |ns| ns[:type] }.uniq
|
||||
ns_ok = redundant_data_for?(nameserver: true, input: result_types)
|
||||
key_ok = redundant_data_for?(nameserver: false, input: data)
|
||||
|
||||
return true if ns_ok && key_ok
|
||||
|
||||
@logger.info "CsyncJob: #{domain}: Reseting state. Reason: " +
|
||||
unqualification_reason(ns_ok, key_ok, result_types)
|
||||
|
||||
CsyncRecord.where(domain: Domain.where(name: domain)).delete_all
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def redundant_data_for?(nameserver: false, input:)
|
||||
if nameserver
|
||||
input.size == 1 && (input & %w[secure insecure]).any?
|
||||
else
|
||||
input[:ns].map { |ns| ns[:cdnskey] }.uniq.size == 1
|
||||
end
|
||||
end
|
||||
|
||||
def unqualification_reason(nss, key, result_types)
|
||||
return 'no CDNSKEY / nameservers reported different CDNSKEYs' unless key
|
||||
|
||||
if result_types.include? 'untrustworthy'
|
||||
return 'current DNSSEC config invalid (required for rollover/delete)'
|
||||
end
|
||||
|
||||
"Nameserver(s) not reachable / invalid data (#{result_types.join(', ')})" unless nss
|
||||
end
|
||||
|
||||
def process_scanner_results
|
||||
scanner_results
|
||||
|
||||
@results.keys.each do |domain|
|
||||
next unless qualified_for_monitoring?(domain, @results[domain])
|
||||
|
||||
CsyncRecord.by_domain_name(domain)&.record_new_scan(@results[domain][:ns].first)
|
||||
end
|
||||
end
|
||||
|
||||
def scanner_results
|
||||
scanner_line_results.each do |fetch|
|
||||
domain_name = fetch[:domain]
|
||||
@results[domain_name] = { ns: [] } unless @results[domain_name]
|
||||
@results[domain_name][:ns] << fetch.except(:domain)
|
||||
end
|
||||
end
|
||||
|
||||
def scanner_line_results
|
||||
records = []
|
||||
File.open(ENV['cdns_scanner_output_file'], 'r').each_line do |line|
|
||||
# Input type, NS host, NS IP, Domain name, Key type, Protocol, Algorithm, Public key
|
||||
data = line.strip.split(' ')
|
||||
if data[0] == 'secure'
|
||||
type, domain, key_bit, proto, alg, pub, ns, ns_ip = data
|
||||
else
|
||||
type, ns, ns_ip, domain, key_bit, proto, alg, pub = data
|
||||
end
|
||||
cdnskey = key_bit && proto && alg && pub ? "#{key_bit} #{proto} #{alg} #{pub}" : nil
|
||||
record = { domain: domain, type: type, ns: ns, ns_ip: ns_ip, flags: key_bit, proto: proto,
|
||||
alg: alg, pub: pub, cdnskey: cdnskey }
|
||||
records << record
|
||||
end
|
||||
records
|
||||
end
|
||||
|
||||
# From this point we're working on generating input for cdnskey-scanner
|
||||
def gather_pollable_domains
|
||||
@logger.info 'CsyncJob Generate: Gathering current domain(s) data'
|
||||
Nameserver.select(:hostname, :domain_id).all.each do |ns|
|
||||
%i[secure insecure].each do |i|
|
||||
@input_store[i][ns.hostname] = [] unless @input_store[i].key? ns.hostname
|
||||
end
|
||||
|
||||
append_domains_to_list(ns)
|
||||
end
|
||||
end
|
||||
|
||||
def append_domains_to_list(nameserver)
|
||||
Domain.where(id: nameserver.domain_id).all.each do |domain|
|
||||
@input_store[domain.dnskeys.any? ? :secure : :insecure][nameserver.hostname].push domain.name
|
||||
end
|
||||
end
|
||||
|
||||
def generate_scanner_input
|
||||
@logger.info 'CsyncJob Generate: Gathering current domain(s) data'
|
||||
gather_pollable_domains
|
||||
|
||||
out_file = File.new(ENV['cdns_scanner_input_file'], 'w+')
|
||||
|
||||
%i[secure insecure].each do |state|
|
||||
out_file.puts "[#{state}]"
|
||||
create_input_lines(out_file, state)
|
||||
end
|
||||
|
||||
out_file.close
|
||||
@logger.info 'CsyncJob Generate: Finished writing output to ' + ENV['cdns_scanner_input_file']
|
||||
end
|
||||
|
||||
def create_input_lines(out_file, state)
|
||||
@input_store[state].keys.each do |nameserver|
|
||||
domains = @input_store[state][nameserver].join(' ')
|
||||
next unless domains.length.positive?
|
||||
|
||||
out_file.puts "#{nameserver} #{domains}"
|
||||
end
|
||||
end
|
||||
end
|
23
app/mailers/csync_mailer.rb
Normal file
23
app/mailers/csync_mailer.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
class CsyncMailer < ApplicationMailer
|
||||
def dnssec_updated(domain:)
|
||||
@domain = domain
|
||||
emails = contact_emails(domain)
|
||||
|
||||
subject = default_i18n_subject(domain_name: domain.name)
|
||||
mail(to: emails, subject: subject)
|
||||
end
|
||||
|
||||
def dnssec_deleted(domain:)
|
||||
@domain = domain
|
||||
emails = contact_emails(domain)
|
||||
|
||||
subject = default_i18n_subject(domain_name: domain.name)
|
||||
mail(to: emails, subject: subject)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def contact_emails(domain)
|
||||
(domain.contacts.map(&:email) << domain.registrant.email).uniq
|
||||
end
|
||||
end
|
81
app/models/actions/contact_create.rb
Normal file
81
app/models/actions/contact_create.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
module Actions
|
||||
class ContactCreate
|
||||
attr_reader :contact, :legal_document, :ident
|
||||
|
||||
def initialize(contact, legal_document, ident)
|
||||
@contact = contact
|
||||
@legal_document = legal_document
|
||||
@ident = ident
|
||||
end
|
||||
|
||||
def call
|
||||
maybe_remove_address
|
||||
maybe_attach_legal_doc
|
||||
validate_ident
|
||||
commit
|
||||
end
|
||||
|
||||
def maybe_remove_address
|
||||
return if Contact.address_processing?
|
||||
|
||||
contact.city = nil
|
||||
contact.zip = nil
|
||||
contact.street = nil
|
||||
contact.state = nil
|
||||
contact.country_code = nil
|
||||
end
|
||||
|
||||
def validate_ident
|
||||
validate_ident_integrity
|
||||
validate_ident_birthday
|
||||
|
||||
identifier = ::Contact::Ident.new(code: ident[:ident], type: ident[:ident_type],
|
||||
country_code: ident[:ident_country_code])
|
||||
|
||||
identifier.validate
|
||||
contact.identifier = identifier
|
||||
end
|
||||
|
||||
def validate_ident_integrity
|
||||
return if ident.blank?
|
||||
|
||||
if ident[:ident_type].blank?
|
||||
contact.add_epp_error('2003', nil, 'ident_type',
|
||||
I18n.t('errors.messages.required_ident_attribute_missing'))
|
||||
@error = true
|
||||
elsif !%w[priv org birthday].include?(ident[:ident_type])
|
||||
contact.add_epp_error('2003', nil, 'ident_type', 'Invalid ident type')
|
||||
@error = true
|
||||
end
|
||||
end
|
||||
|
||||
def validate_ident_birthday
|
||||
return if ident.blank?
|
||||
return unless ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank?
|
||||
|
||||
contact.add_epp_error('2003', nil, 'ident_country_code',
|
||||
I18n.t('errors.messages.required_ident_attribute_missing'))
|
||||
@error = true
|
||||
end
|
||||
|
||||
def maybe_attach_legal_doc
|
||||
return unless legal_document
|
||||
|
||||
doc = LegalDocument.create(
|
||||
documentable_type: Contact,
|
||||
document_type: legal_document[:type], body: legal_document[:body]
|
||||
)
|
||||
|
||||
contact.legal_documents = [doc]
|
||||
contact.legal_document_id = doc.id
|
||||
end
|
||||
|
||||
def commit
|
||||
contact.id = nil # new record
|
||||
return false if @error
|
||||
|
||||
contact.generate_code
|
||||
contact.save
|
||||
end
|
||||
end
|
||||
end
|
46
app/models/actions/contact_delete.rb
Normal file
46
app/models/actions/contact_delete.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
module Actions
|
||||
class ContactDelete
|
||||
attr_reader :contact
|
||||
attr_reader :new_attributes
|
||||
attr_reader :legal_document
|
||||
attr_reader :ident
|
||||
attr_reader :user
|
||||
|
||||
def initialize(contact, legal_document = nil)
|
||||
@legal_document = legal_document
|
||||
@contact = contact
|
||||
end
|
||||
|
||||
def call
|
||||
maybe_attach_legal_doc
|
||||
|
||||
if contact.linked?
|
||||
contact.errors.add(:domains, :exist)
|
||||
return
|
||||
end
|
||||
|
||||
if contact.delete_prohibited?
|
||||
contact.errors.add(:statuses, :delete_prohibited)
|
||||
return
|
||||
end
|
||||
|
||||
commit
|
||||
end
|
||||
|
||||
def maybe_attach_legal_doc
|
||||
return unless legal_document
|
||||
|
||||
document = contact.legal_documents.create(
|
||||
document_type: legal_document[:type],
|
||||
body: legal_document[:body]
|
||||
)
|
||||
|
||||
contact.legal_document_id = document.id
|
||||
contact.save
|
||||
end
|
||||
|
||||
def commit
|
||||
contact.destroy
|
||||
end
|
||||
end
|
||||
end
|
109
app/models/actions/contact_update.rb
Normal file
109
app/models/actions/contact_update.rb
Normal file
|
@ -0,0 +1,109 @@
|
|||
module Actions
|
||||
class ContactUpdate
|
||||
attr_reader :contact
|
||||
attr_reader :new_attributes
|
||||
attr_reader :legal_document
|
||||
attr_reader :ident
|
||||
attr_reader :user
|
||||
|
||||
def initialize(contact, new_attributes, legal_document, ident, user)
|
||||
@contact = contact
|
||||
@new_attributes = new_attributes
|
||||
@legal_document = legal_document
|
||||
@ident = ident
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
maybe_remove_address
|
||||
maybe_update_statuses
|
||||
maybe_update_ident if ident.present?
|
||||
maybe_attach_legal_doc
|
||||
commit
|
||||
end
|
||||
|
||||
def maybe_remove_address
|
||||
return if Contact.address_processing?
|
||||
|
||||
new_attributes.delete(:city)
|
||||
new_attributes.delete(:zip)
|
||||
new_attributes.delete(:street)
|
||||
new_attributes.delete(:state)
|
||||
new_attributes.delete(:country_code)
|
||||
end
|
||||
|
||||
def maybe_update_statuses
|
||||
return unless Setting.client_status_editing_enabled
|
||||
|
||||
new_statuses =
|
||||
contact.statuses - new_attributes[:statuses_to_remove] + new_attributes[:statuses_to_add]
|
||||
|
||||
new_attributes[:statuses] = new_statuses
|
||||
end
|
||||
|
||||
def maybe_attach_legal_doc
|
||||
return unless legal_document
|
||||
|
||||
document = contact.legal_documents.create(
|
||||
document_type: legal_document[:type],
|
||||
body: legal_document[:body]
|
||||
)
|
||||
|
||||
contact.legal_document_id = document.id
|
||||
end
|
||||
|
||||
def maybe_update_ident
|
||||
unless ident.is_a?(Hash)
|
||||
contact.add_epp_error('2308', nil, nil, I18n.t('epp.contacts.errors.valid_ident'))
|
||||
@error = true
|
||||
return
|
||||
end
|
||||
|
||||
if contact.identifier.valid?
|
||||
submitted_ident = ::Contact::Ident.new(code: ident[:ident],
|
||||
type: ident[:ident_type],
|
||||
country_code: ident[:ident_country_code])
|
||||
|
||||
if submitted_ident != contact.identifier
|
||||
contact.add_epp_error('2308', nil, nil, I18n.t('epp.contacts.errors.valid_ident'))
|
||||
@error = true
|
||||
end
|
||||
else
|
||||
ident_update_attempt = ident[:ident] != contact.ident
|
||||
|
||||
if ident_update_attempt
|
||||
contact.add_epp_error('2308', nil, nil, I18n.t('epp.contacts.errors.ident_update'))
|
||||
@error = true
|
||||
end
|
||||
|
||||
identifier = ::Contact::Ident.new(code: ident[:ident],
|
||||
type: ident[:ident_type],
|
||||
country_code: ident[:ident_country_code])
|
||||
|
||||
identifier.validate
|
||||
|
||||
contact.identifier = identifier
|
||||
contact.ident_updated_at ||= Time.zone.now
|
||||
end
|
||||
end
|
||||
|
||||
def commit
|
||||
return false if @error
|
||||
|
||||
contact.upid = user.registrar&.id
|
||||
contact.up_date = Time.zone.now
|
||||
|
||||
contact.attributes = new_attributes
|
||||
|
||||
email_changed = contact.will_save_change_to_email?
|
||||
old_email = contact.email_was
|
||||
updated = contact.save
|
||||
|
||||
if updated && email_changed && contact.registrant?
|
||||
ContactMailer.email_changed(contact: contact, old_email: old_email).deliver_now
|
||||
end
|
||||
|
||||
updated
|
||||
end
|
||||
end
|
||||
end
|
74
app/models/actions/domain_transfer.rb
Normal file
74
app/models/actions/domain_transfer.rb
Normal file
|
@ -0,0 +1,74 @@
|
|||
module Actions
|
||||
class DomainTransfer
|
||||
attr_reader :domain
|
||||
attr_reader :transfer_code
|
||||
attr_reader :legal_document
|
||||
attr_reader :ident
|
||||
attr_reader :user
|
||||
|
||||
def initialize(domain, transfer_code, user)
|
||||
@domain = domain
|
||||
@transfer_code = transfer_code
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
return unless domain_exists?
|
||||
return unless valid_transfer_code?
|
||||
|
||||
run_validations
|
||||
|
||||
# return domain.pending_transfer if domain.pending_transfer
|
||||
# attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call)
|
||||
|
||||
return if domain.errors[:epp_errors].any?
|
||||
|
||||
commit
|
||||
end
|
||||
|
||||
def domain_exists?
|
||||
return true if domain.persisted?
|
||||
|
||||
domain.add_epp_error('2303', nil, nil, 'Object does not exist')
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def run_validations
|
||||
validate_registrar
|
||||
validate_eligilibty
|
||||
validate_not_discarded
|
||||
end
|
||||
|
||||
def valid_transfer_code?
|
||||
return true if transfer_code == domain.transfer_code
|
||||
|
||||
domain.add_epp_error('2202', nil, nil, 'Invalid authorization information')
|
||||
false
|
||||
end
|
||||
|
||||
def validate_registrar
|
||||
return unless user == domain.registrar
|
||||
|
||||
domain.add_epp_error('2002', nil, nil,
|
||||
I18n.t(:domain_already_belongs_to_the_querying_registrar))
|
||||
end
|
||||
|
||||
def validate_eligilibty
|
||||
return unless domain.non_transferable?
|
||||
|
||||
domain.add_epp_error('2304', nil, nil, 'Object status prohibits operation')
|
||||
end
|
||||
|
||||
def validate_not_discarded
|
||||
return unless domain.discarded?
|
||||
|
||||
domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer')
|
||||
end
|
||||
|
||||
def commit
|
||||
bare_domain = Domain.find(domain.id)
|
||||
::DomainTransfer.request(bare_domain, user)
|
||||
end
|
||||
end
|
||||
end
|
45
app/models/concerns/csync_record/diggable.rb
Normal file
45
app/models/concerns/csync_record/diggable.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
module CsyncRecord::Diggable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def valid_security_level?(post: false)
|
||||
res = post ? valid_post_action? : valid_pre_action?
|
||||
|
||||
log_dnssec_entry(valid: res, post: post)
|
||||
res
|
||||
rescue Dnsruby::NXDomain
|
||||
log.info("CsyncRecord: #{domain.name}: Could not resolve (NXDomain)")
|
||||
false
|
||||
end
|
||||
|
||||
def valid_pre_action?
|
||||
case domain.dnssec_security_level
|
||||
when Dnsruby::Message::SecurityLevel.SECURE
|
||||
return true if %w[rollover deactivate].include?(action)
|
||||
when Dnsruby::Message::SecurityLevel.INSECURE, Dnsruby::Message::SecurityLevel.BOGUS
|
||||
return true if action == 'initialized'
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def valid_post_action?
|
||||
secure_msg = Dnsruby::Message::SecurityLevel.SECURE
|
||||
security_level = domain.dnssec_security_level(stubber: dnskey)
|
||||
return true if action == 'deactivate' && security_level != secure_msg
|
||||
return true if %w[rollover initialized].include?(action) && security_level == secure_msg
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def dnssec_validates?
|
||||
return false unless dnskey.valid?
|
||||
return true if valid_security_level? && valid_security_level?(post: true)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def log_dnssec_entry(valid:, post:)
|
||||
log.info("#{domain.name}: #{post ? 'Post' : 'Pre'} DNSSEC validation " \
|
||||
"#{valid ? 'PASSED' : 'FAILED'} for action '#{action}'")
|
||||
end
|
||||
end
|
132
app/models/csync_record.rb
Normal file
132
app/models/csync_record.rb
Normal file
|
@ -0,0 +1,132 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CsyncRecord < ApplicationRecord
|
||||
include CsyncRecord::Diggable
|
||||
belongs_to :domain, optional: false
|
||||
validates :domain, uniqueness: true
|
||||
validates :cdnskey, :action, :last_scan, presence: true
|
||||
validate :validate_unique_pub_key
|
||||
validate :validate_csync_action
|
||||
validate :validate_cdnskey_format
|
||||
after_save :process_new_dnskey, if: proc { pushable? && !disable_requested? }
|
||||
after_save :remove_dnskeys, if: proc { pushable? && disable_requested? }
|
||||
|
||||
SCAN_CYCLES = 3
|
||||
|
||||
def record_new_scan(result)
|
||||
assign_scanner_data!(result)
|
||||
prefix = "CsyncRecord: #{domain.name}:"
|
||||
|
||||
if save
|
||||
log.info "#{prefix} Cycle done."
|
||||
else
|
||||
log.info "#{prefix}: not processing. Reason: #{errors.full_messages.join(' .')}"
|
||||
CsyncRecord.where(domain: domain).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
def assign_scanner_data!(result)
|
||||
state = result[:type]
|
||||
self.last_scan = Time.zone.now
|
||||
self.times_scanned = (persisted? && cdnskey != result[:cdnskey] ? 1 : times_scanned + 1)
|
||||
self.cdnskey = result[:cdnskey]
|
||||
self.action = initializes_dnssec?(state) ? 'initialized' : determine_csync_intention(state)
|
||||
end
|
||||
|
||||
def dnskey
|
||||
key = Dnskey.new_from_csync(domain: domain, cdnskey: cdnskey)
|
||||
log.info "DNSKEY not valid. #{key.errors.full_messages.join('. ')}." unless key.valid?
|
||||
|
||||
key
|
||||
end
|
||||
|
||||
def destroy_all_but_last_one
|
||||
domain.dnskeys.order(id: :desc).offset(1).destroy_all
|
||||
end
|
||||
|
||||
def process_new_dnskey
|
||||
return unless dnssec_validates?
|
||||
|
||||
if dnskey.save
|
||||
destroy_all_but_last_one
|
||||
finalize_and_notify
|
||||
else
|
||||
log.info "Failed to save DNSKEY. Errors: #{dnskey.errors.full_messages.join('. ')}"
|
||||
end
|
||||
end
|
||||
|
||||
def finalize_and_notify
|
||||
CsyncMailer.dnssec_updated(domain: domain).deliver_now
|
||||
notify_registrar_about_csync
|
||||
CsyncRecord.where(domain: domain).destroy_all
|
||||
log.info "CsyncRecord: #{domain.name}: DNSKEYs updated."
|
||||
end
|
||||
|
||||
def pushable?
|
||||
return true if domain.dnskeys.any? || times_scanned >= SCAN_CYCLES
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def disable_requested?
|
||||
['0 3 0 AA==', '0 3 0 0'].include? cdnskey
|
||||
end
|
||||
|
||||
def remove_dnskeys
|
||||
log.info "CsyncJob: Removing DNSKEYs for domain '#{domain.name}'"
|
||||
domain.dnskeys.destroy_all
|
||||
CsyncMailer.dnssec_deleted(domain: domain).deliver_now
|
||||
notify_registrar_about_csync
|
||||
|
||||
destroy
|
||||
end
|
||||
|
||||
def notify_registrar_about_csync
|
||||
domain.update_whois_record
|
||||
domain.registrar.notifications.create!(text: I18n.t('notifications.texts.csync',
|
||||
domain: domain.name, action: action))
|
||||
end
|
||||
|
||||
def validate_unique_pub_key
|
||||
return false unless domain
|
||||
return true if disable_requested?
|
||||
return true unless domain.dnskeys.where(public_key: dnskey.public_key).any?
|
||||
|
||||
errors.add(:public_key, 'already tied to this domain')
|
||||
end
|
||||
|
||||
def self.by_domain_name(domain_name)
|
||||
domain = Domain.find_by(name: domain_name)
|
||||
log.info "CsyncRecord: '#{domain_name}' not in zone. Not initializing record." unless domain
|
||||
CsyncRecord.find_or_initialize_by(domain: domain) if domain
|
||||
end
|
||||
|
||||
def determine_csync_intention(scan_state)
|
||||
return unless domain.dnskeys.any? && scan_state == 'secure'
|
||||
|
||||
disable_requested? ? 'deactivate' : 'rollover'
|
||||
end
|
||||
|
||||
def initializes_dnssec?(scan_state)
|
||||
true if domain.dnskeys.empty? && !disable_requested? && scan_state == 'insecure'
|
||||
end
|
||||
|
||||
def log
|
||||
@log ||= Rails.env.test? ? logger : Logger.new(STDOUT)
|
||||
@log
|
||||
end
|
||||
|
||||
def validate_csync_action
|
||||
return true if %w[initialized rollover].include? action
|
||||
return true if action == 'deactivate' && disable_requested?
|
||||
|
||||
errors.add(:action, :invalid)
|
||||
end
|
||||
|
||||
def validate_cdnskey_format
|
||||
return true if disable_requested?
|
||||
return true if dnskey.valid?
|
||||
|
||||
errors.add(:cdnskey, :invalid)
|
||||
end
|
||||
end
|
|
@ -28,7 +28,7 @@ class Dnskey < ApplicationRecord
|
|||
PROTOCOLS = %w(3)
|
||||
FLAGS = %w(0 256 257) # 256 = ZSK, 257 = KSK
|
||||
DS_DIGEST_TYPE = [1,2]
|
||||
|
||||
RESOLVERS = ENV['dnssec_resolver_ips'].to_s.strip.split(', ').freeze
|
||||
self.ignored_columns = %w[legacy_domain_id]
|
||||
|
||||
def epp_code_map
|
||||
|
@ -122,6 +122,26 @@ class Dnskey < ApplicationRecord
|
|||
errors.add(:public_key, :invalid)
|
||||
end
|
||||
|
||||
def self.new_from_csync(cdnskey:, domain:)
|
||||
cdnskey ||= '' # avoid strip() issues for gibberish key
|
||||
|
||||
flags, proto, alg, pub = cdnskey.strip.split(' ')
|
||||
Dnskey.new(domain: domain, flags: flags, protocol: proto, alg: alg, public_key: pub)
|
||||
end
|
||||
|
||||
def ds_rr
|
||||
# Break the DNSSEC trust chain as we are not able to fake RRSIG's
|
||||
Dnsruby::Dnssec.clear_trust_anchors
|
||||
Dnsruby::Dnssec.clear_trusted_keys
|
||||
|
||||
# Basically let's configure domain as root anchor. We can still verify
|
||||
# RRSIG's / DNSKEY targeted by DS of this domain
|
||||
generate_digest
|
||||
generate_ds_key_tag
|
||||
Dnsruby::RR.create("#{domain.name}. 3600 IN DS #{ds_key_tag} #{ds_alg} " \
|
||||
"#{ds_digest_type} #{ds_digest}")
|
||||
end
|
||||
|
||||
class << self
|
||||
def int_to_hex(s)
|
||||
s = s.to_s(16)
|
||||
|
|
|
@ -58,6 +58,7 @@ class Domain < ApplicationRecord
|
|||
|
||||
has_many :legal_documents, as: :documentable
|
||||
has_many :registrant_verifications, dependent: :destroy
|
||||
has_one :csync_record, dependent: :destroy
|
||||
|
||||
after_initialize do
|
||||
self.pending_json = {} if pending_json.blank?
|
||||
|
@ -167,6 +168,35 @@ class Domain < ApplicationRecord
|
|||
validate :validate_nameserver_ips
|
||||
|
||||
validate :statuses_uniqueness
|
||||
|
||||
def security_level_resolver
|
||||
resolver = Dnsruby::Resolver.new(nameserver: Dnskey::RESOLVERS)
|
||||
resolver.do_validation = true
|
||||
resolver.do_caching = false
|
||||
resolver.dnssec = true
|
||||
resolver
|
||||
end
|
||||
|
||||
def dnssec_security_level(stubber: nil)
|
||||
Dnsruby::Dnssec.reset
|
||||
resolver = security_level_resolver
|
||||
Dnsruby::Recursor.clear_caches(resolver)
|
||||
if Rails.env.staging?
|
||||
clear_dnssec_trusted_anchors_and_keys
|
||||
elsif stubber
|
||||
Dnsruby::Dnssec.add_trust_anchor(stubber.ds_rr)
|
||||
end
|
||||
recursor = Dnsruby::Recursor.new(resolver)
|
||||
recursor.dnssec = true
|
||||
recursor.query(name, 'A', 'IN').security_level
|
||||
end
|
||||
|
||||
def clear_dnssec_trusted_anchors_and_keys
|
||||
Dnsruby::Dnssec.clear_trust_anchors
|
||||
Dnsruby::Dnssec.clear_trusted_keys
|
||||
Dnsruby::Dnssec.add_trust_anchor(Dnsruby::RR.create(ENV['trusted_dnskey']))
|
||||
end
|
||||
|
||||
def statuses_uniqueness
|
||||
return if statuses.uniq == statuses
|
||||
errors.add(:statuses, :taken)
|
||||
|
|
|
@ -44,6 +44,8 @@ class Epp::Contact < Contact
|
|||
|
||||
def check_availability(codes, reg:)
|
||||
codes = [codes] if codes.is_a?(String)
|
||||
codes = codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" }
|
||||
|
||||
res = []
|
||||
codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" }.map { |c| c.strip.upcase }.each do |x|
|
||||
c = find_by_epp_code(x)
|
||||
|
|
|
@ -58,8 +58,8 @@ class Nameserver < ApplicationRecord
|
|||
end
|
||||
|
||||
def hostname=(hostname)
|
||||
self[:hostname] = SimpleIDN.to_unicode(hostname)
|
||||
self[:hostname_puny] = SimpleIDN.to_ascii(hostname)
|
||||
self[:hostname] = SimpleIDN.to_unicode(hostname).gsub(/\.+$/, '')
|
||||
self[:hostname_puny] = SimpleIDN.to_ascii(hostname).gsub(/\.+$/, '')
|
||||
end
|
||||
|
||||
class << self
|
||||
|
|
14
app/views/mailers/csync_mailer/dnssec_deleted.html.erb
Normal file
14
app/views/mailers/csync_mailer/dnssec_deleted.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
|||
Tere,
|
||||
<br><br>
|
||||
Oleme eemaldanud Teie domeeni <%= @domain.name %> DNSSEC kirjed registrist.
|
||||
|
||||
Domeeni <%= @domain.name %> DNSSEC on nüüd välja lülitatud.
|
||||
<%= render 'mailers/shared/signatures/signature.et.html' %>
|
||||
<hr>
|
||||
<br><br>
|
||||
Hi,
|
||||
<br><br>
|
||||
We have removed DNSSEC data in our registry for domain <%= @domain.name %>.
|
||||
<br>
|
||||
DNSSEC has been turned off for <%= @domain.name %> as of now.
|
||||
<%= render 'mailers/shared/signatures/signature.en.html' %>
|
14
app/views/mailers/csync_mailer/dnssec_updated.html.erb
Normal file
14
app/views/mailers/csync_mailer/dnssec_updated.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
|||
Tere,
|
||||
<br><br>
|
||||
Oleme uuendatud Teie domeeni <%= @domain.name %> DNSSEC andmeid registris.
|
||||
<br>
|
||||
Lisasime CDNSKEY väärtuses kajastatud võtme oma tsoonifaili ning on nüüd aktiveeritud.
|
||||
<%= render 'mailers/shared/signatures/signature.et.html' %>
|
||||
<hr>
|
||||
<br><br>
|
||||
Hi,
|
||||
<br><br>
|
||||
We have updated DNSSEC data in our registry for domain <%= @domain.name %>.
|
||||
<br>
|
||||
DNS key specified in CDNSKEY has been added to our zone file and is now taking effect.
|
||||
<%= render 'mailers/shared/signatures/signature.en.html' %>
|
|
@ -39,8 +39,9 @@ ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem'
|
|||
ca_key_password: 'your-root-key-password'
|
||||
|
||||
directo_invoice_url: 'https://domain/ddddd.asp'
|
||||
|
||||
|
||||
cdns_scanner_input_file: '/opt/cdns/input.txt'
|
||||
cdns_scanner_output_file: '/opt/cdns/output.txt'
|
||||
dnssec_resolver_ips: 8.8.8.8, 8.8.4.4
|
||||
#
|
||||
# EPP
|
||||
#
|
||||
|
@ -192,6 +193,10 @@ test:
|
|||
action_mailer_force_delete_from: 'legal@registry.test'
|
||||
lhv_p12_keystore: 'test/fixtures/files/keystore.p12'
|
||||
lhv_keystore_password: 'testtest'
|
||||
lhv_keystore_alias: 'testtest'
|
||||
cdns_scanner_input_file: 'tmp/cdns_input.txt'
|
||||
cdns_scanner_output_file: 'test/fixtures/files/cdns_output.txt'
|
||||
dnssec_resolver_ips: 8.8.8.8, 8.8.4.4
|
||||
legal_documents_dir: 'test/fixtures/files'
|
||||
|
||||
# Airbrake // Errbit:
|
||||
|
|
|
@ -16,6 +16,7 @@ en:
|
|||
disputed_domains: Disputed domains
|
||||
bulk_actions: Bulk actions
|
||||
bounced_email_addresses: Bounced emails
|
||||
mass_actions: Mass actions
|
||||
epp_log: EPP log
|
||||
repp_log: REPP log
|
||||
que: Que
|
||||
|
|
|
@ -6,3 +6,4 @@ en:
|
|||
reset_btn: Reset
|
||||
show:
|
||||
title: REPP log
|
||||
|
||||
|
|
10
config/locales/mailers/csync.en.yml
Normal file
10
config/locales/mailers/csync.en.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
en:
|
||||
csync_mailer:
|
||||
dnssec_updated:
|
||||
subject: >-
|
||||
Teie domeeni %{domain_name} DNSSEC andmed on uuendatud
|
||||
/ DNSSEC data for %{domain_name} has been updated
|
||||
dnssec_deleted:
|
||||
subject: >-
|
||||
Teie domeeni %{domain_name} DNSSEC andmed on eemaldatud
|
||||
/ DNSSEC data for %{domain_name} has been removed
|
|
@ -6,5 +6,6 @@ en:
|
|||
It was associated with registrant %{old_registrant_code}
|
||||
and contacts %{old_contacts_codes}.
|
||||
contact_update: Contact %{contact} has been updated by registrant
|
||||
csync: CSYNC DNSSEC %{action} for domain %{domain}
|
||||
registrar_locked: Domain %{domain_name} has been locked by registrant
|
||||
registrar_unlocked: Domain %{domain_name} has been unlocked by registrant
|
||||
|
|
12
db/migrate/20200605125332_create_csync_records.rb
Normal file
12
db/migrate/20200605125332_create_csync_records.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class CreateCsyncRecords < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :csync_records do |t|
|
||||
t.belongs_to :domain, foreign_key: true, null: false, index: { unique: true }
|
||||
t.string :cdnskey, null: false
|
||||
t.string :action, null: false
|
||||
t.integer :times_scanned, null: false, default: 0
|
||||
t.datetime :last_scan, null: false
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
398
db/structure.sql
398
db/structure.sql
|
@ -1988,6 +1988,40 @@ CREATE TABLE public.log_users (
|
|||
uuid character varying
|
||||
);
|
||||
|
||||
--
|
||||
-- Name: csync_records; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.csync_records (
|
||||
id bigint NOT NULL,
|
||||
domain_id bigint NOT NULL,
|
||||
cdnskey character varying NOT NULL,
|
||||
action character varying NOT NULL,
|
||||
times_scanned integer DEFAULT 0 NOT NULL,
|
||||
last_scan timestamp without time zone NOT NULL,
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: csync_records_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.csync_records_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: csync_records_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.csync_records_id_seq OWNED BY public.csync_records.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_users_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
|
@ -2251,6 +2285,12 @@ CREATE TABLE public.registrant_verifications (
|
|||
action character varying NOT NULL,
|
||||
domain_id integer NOT NULL,
|
||||
action_type character varying NOT NULL,
|
||||
creator_id integer,
|
||||
updater_id integer,
|
||||
session character varying,
|
||||
children json,
|
||||
ident_updated_at timestamp without time zone,
|
||||
uuid character varying,
|
||||
creator_str character varying,
|
||||
updator_str character varying
|
||||
);
|
||||
|
@ -3017,21 +3057,40 @@ ALTER TABLE ONLY public.que_jobs ALTER COLUMN job_id SET DEFAULT nextval('public
|
|||
|
||||
ALTER TABLE ONLY public.registrant_verifications ALTER COLUMN id SET DEFAULT nextval('public.registrant_verifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.accounts ALTER COLUMN id SET DEFAULT nextval('public.accounts_id_seq'::regclass);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.log_invoices
|
||||
ADD CONSTRAINT log_invoices_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.registrars ALTER COLUMN id SET DEFAULT nextval('public.registrars_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
ALTER TABLE ONLY public.disputes ALTER COLUMN id SET DEFAULT nextval('public.disputes_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.log_nameservers
|
||||
ADD CONSTRAINT log_nameservers_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.reserved_domains ALTER COLUMN id SET DEFAULT nextval('public.reserved_domains_id_seq'::regclass);
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.setting_entries ALTER COLUMN id SET DEFAULT nextval('public.setting_entries_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -3050,6 +3109,285 @@ ALTER TABLE ONLY public.settings ALTER COLUMN id SET DEFAULT nextval('public.set
|
|||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.domains ALTER COLUMN id SET DEFAULT nextval('public.domains_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: versions id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.email_address_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_address_verifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: white_ips id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.email_addresses_validations ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_validations_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: whois_records id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.email_addresses_verifications ALTER COLUMN id SET DEFAULT nextval('public.email_addresses_verifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: zones id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.epp_sessions ALTER COLUMN id SET DEFAULT nextval('public.epp_sessions_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.invoice_items ALTER COLUMN id SET DEFAULT nextval('public.invoice_items_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: invoices id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.invoices ALTER COLUMN id SET DEFAULT nextval('public.invoices_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: legal_documents id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.legal_documents ALTER COLUMN id SET DEFAULT nextval('public.legal_documents_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_account_activities id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_account_activities ALTER COLUMN id SET DEFAULT nextval('public.log_account_activities_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_accounts id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_accounts ALTER COLUMN id SET DEFAULT nextval('public.log_accounts_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_actions id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_actions ALTER COLUMN id SET DEFAULT nextval('public.log_actions_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_bank_statements id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_bank_statements ALTER COLUMN id SET DEFAULT nextval('public.log_bank_statements_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_bank_transactions id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_bank_transactions ALTER COLUMN id SET DEFAULT nextval('public.log_bank_transactions_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_blocked_domains id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_blocked_domains ALTER COLUMN id SET DEFAULT nextval('public.log_blocked_domains_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_certificates id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_certificates ALTER COLUMN id SET DEFAULT nextval('public.log_certificates_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_contacts id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_contacts ALTER COLUMN id SET DEFAULT nextval('public.log_contacts_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_dnskeys id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_dnskeys ALTER COLUMN id SET DEFAULT nextval('public.log_dnskeys_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_domain_contacts id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_domain_contacts ALTER COLUMN id SET DEFAULT nextval('public.log_domain_contacts_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_domains id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_domains ALTER COLUMN id SET DEFAULT nextval('public.log_domains_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_invoice_items id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_invoice_items ALTER COLUMN id SET DEFAULT nextval('public.log_invoice_items_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_invoices id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_invoices ALTER COLUMN id SET DEFAULT nextval('public.log_invoices_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_nameservers id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_nameservers ALTER COLUMN id SET DEFAULT nextval('public.log_nameservers_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_notifications id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_notifications ALTER COLUMN id SET DEFAULT nextval('public.log_notifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_payment_orders id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_payment_orders ALTER COLUMN id SET DEFAULT nextval('public.log_payment_orders_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_registrant_verifications id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_registrant_verifications ALTER COLUMN id SET DEFAULT nextval('public.log_registrant_verifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_registrars id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_registrars ALTER COLUMN id SET DEFAULT nextval('public.log_registrars_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_reserved_domains id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_reserved_domains ALTER COLUMN id SET DEFAULT nextval('public.log_reserved_domains_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_settings id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_settings ALTER COLUMN id SET DEFAULT nextval('public.log_settings_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_users id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_users ALTER COLUMN id SET DEFAULT nextval('public.log_users_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_white_ips id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_white_ips ALTER COLUMN id SET DEFAULT nextval('public.log_white_ips_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: nameservers id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.nameservers ALTER COLUMN id SET DEFAULT nextval('public.nameservers_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: notifications id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: payment_orders id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: prices id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.prices ALTER COLUMN id SET DEFAULT nextval('public.prices_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: que_jobs job_id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.que_jobs ALTER COLUMN job_id SET DEFAULT nextval('public.que_jobs_job_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registrant_verifications id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.registrant_verifications ALTER COLUMN id SET DEFAULT nextval('public.registrant_verifications_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registrars id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.registrars ALTER COLUMN id SET DEFAULT nextval('public.registrars_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: reserved_domains id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.reserved_domains ALTER COLUMN id SET DEFAULT nextval('public.reserved_domains_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: settings id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.settings ALTER COLUMN id SET DEFAULT nextval('public.settings_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.log_prices
|
||||
ADD CONSTRAINT log_prices_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
|
||||
|
||||
|
||||
|
@ -3369,22 +3707,6 @@ ALTER TABLE ONLY public.log_invoice_items
|
|||
ADD CONSTRAINT log_invoice_items_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_invoices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_invoices
|
||||
ADD CONSTRAINT log_invoices_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_nameservers_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_nameservers
|
||||
ADD CONSTRAINT log_nameservers_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||
--
|
||||
|
@ -3401,14 +3723,6 @@ ALTER TABLE ONLY public.log_payment_orders
|
|||
ADD CONSTRAINT log_payment_orders_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.log_prices
|
||||
ADD CONSTRAINT log_prices_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -4006,6 +4320,17 @@ CREATE INDEX index_log_contacts_on_whodunnit ON public.log_contacts USING btree
|
|||
CREATE INDEX index_log_dnskeys_on_item_type_and_item_id ON public.log_dnskeys USING btree (item_type, item_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: csync_records id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
ALTER TABLE ONLY public.csync_records ALTER COLUMN id SET DEFAULT nextval('public.csync_records_id_seq'::regclass);
|
||||
|
||||
--
|
||||
-- Name: index_csync_records_on_domain_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX index_csync_records_on_domain_id ON public.csync_records USING btree (domain_id);
|
||||
|
||||
--
|
||||
-- Name: index_log_dnskeys_on_whodunnit; Type: INDEX; Schema: public; Owner: -; Tablespace:
|
||||
--
|
||||
|
@ -4313,9 +4638,8 @@ CREATE UNIQUE INDEX unique_data_migrations ON public.data_migrations USING btree
|
|||
|
||||
CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version);
|
||||
|
||||
|
||||
--
|
||||
-- Name: contacts_registrar_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
-- Name: contacts contacts_registrar_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.contacts
|
||||
|
@ -4521,6 +4845,19 @@ ALTER TABLE ONLY public.nameservers
|
|||
ALTER TABLE ONLY public.users
|
||||
ADD CONSTRAINT user_registrar_id_fk FOREIGN KEY (registrar_id) REFERENCES public.registrars(id);
|
||||
|
||||
--
|
||||
-- Name: csync_records csync_records_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.csync_records
|
||||
ADD CONSTRAINT csync_records_pkey PRIMARY KEY (id);
|
||||
|
||||
--
|
||||
-- Name: csync_records fk_rails_5df85aeb13; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.csync_records
|
||||
ADD CONSTRAINT fk_rails_5df85aeb13 FOREIGN KEY (domain_id) REFERENCES public.domains(id);
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
|
@ -4951,6 +5288,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20200605100827'),
|
||||
('20200610090110'),
|
||||
('20200630081231'),
|
||||
('20200605125332'),
|
||||
('20200714115338'),
|
||||
('20200807110611'),
|
||||
('20200811074839'),
|
||||
|
@ -4961,6 +5299,6 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20200910085157'),
|
||||
('20200910102028'),
|
||||
('20200916125326'),
|
||||
('20210215101019');
|
||||
|
||||
|
||||
('20200917104213'),
|
||||
('20210215101019'),
|
||||
('20200921084356');
|
||||
|
|
10
test/fixtures/dnskeys.yml
vendored
Normal file
10
test/fixtures/dnskeys.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
one:
|
||||
domain:
|
||||
flags: 257
|
||||
protocol: 3
|
||||
alg: 13
|
||||
public_key: mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==
|
||||
ds_key_tag: 2371
|
||||
ds_alg: 13
|
||||
ds_digest_type: 2
|
||||
ds_digest: 39456058862EA09DD96992ED2BDAFAEDE8C7E949589E3DA903A46F4F9CD373EA
|
4
test/fixtures/files/cdns_output.txt
vendored
Normal file
4
test/fixtures/files/cdns_output.txt
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
insecure-empty ns1.bestnames.test 127.0.0.1 airport.test
|
||||
insecure-empty ns2.bestnames.test 127.0.0.1 airport.test
|
||||
insecure ns1.bestnames.test 127.0.0.1 shop.test 257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==
|
||||
insecure ns2.bestnames.test 127.0.0.1 shop.test 257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==
|
73
test/jobs/csync_job_test.rb
Normal file
73
test/jobs/csync_job_test.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
require 'test_helper'
|
||||
|
||||
class CsyncJobTest < ActiveSupport::TestCase
|
||||
include ActionMailer::TestHelper
|
||||
|
||||
setup do
|
||||
@dnskey = dnskeys(:one)
|
||||
@domain = domains(:shop)
|
||||
end
|
||||
|
||||
def test_generates_input_file_for_cdnskey_scanner
|
||||
@dnskey.update(domain: domains(:shop))
|
||||
|
||||
expected_contents = "[secure]\nns1.bestnames.test shop.test\nns2.bestnames.test shop.test\n" \
|
||||
"[insecure]\nns1.bestnames.test airport.test metro.test\nns2.bestnames.test airport.test\n"
|
||||
|
||||
CsyncJob.run(generate: true)
|
||||
|
||||
assert_equal expected_contents, IO.read(ENV['cdns_scanner_input_file'])
|
||||
end
|
||||
|
||||
def test_creates_csync_record_when_new_cdnskey_discovered
|
||||
assert_nil @domain.csync_record
|
||||
CsyncJob.run
|
||||
|
||||
@domain.reload
|
||||
assert @domain.csync_record
|
||||
csync_record = @domain.csync_record
|
||||
assert_equal 1, csync_record.times_scanned
|
||||
assert_equal '257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==', csync_record.cdnskey
|
||||
|
||||
assert_not @domain.dnskeys.any?
|
||||
end
|
||||
|
||||
def test_creates_dnskey_after_required_cycles
|
||||
assert_equal 0, @domain.dnskeys.count
|
||||
assert_nil @domain.csync_record
|
||||
CsyncJob.run # Creates initial CsyncRecord for domain
|
||||
|
||||
@domain.reload
|
||||
assert @domain.csync_record.present?
|
||||
|
||||
@domain.csync_record.update(times_scanned: 2) # 3rd time trigger DNSKEY push
|
||||
assert_equal 0, @domain.dnskeys.count
|
||||
assert_equal 2, @domain.csync_record.times_scanned
|
||||
|
||||
CsyncRecord.stub :by_domain_name, @domain.csync_record do
|
||||
@domain.csync_record.stub :dnssec_validates?, true do
|
||||
CsyncJob.run
|
||||
end
|
||||
end
|
||||
|
||||
@domain.reload
|
||||
assert_equal 1, @domain.dnskeys.count
|
||||
assert_equal 'mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==', @domain.dnskeys.last.public_key
|
||||
assert_nil @domain.csync_record
|
||||
end
|
||||
|
||||
def test_sends_mail_to_contacts_if_dnskey_updated
|
||||
assert_emails 1 do
|
||||
CsyncJob.run
|
||||
@domain.reload
|
||||
|
||||
CsyncRecord.stub :by_domain_name, @domain.csync_record do
|
||||
@domain.csync_record.stub :dnssec_validates?, true do
|
||||
2.times do
|
||||
CsyncJob.run
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -291,37 +291,45 @@ class ContactTest < ActiveSupport::TestCase
|
|||
assert_equal 'US', contact.country_code
|
||||
end
|
||||
|
||||
def test_normalizes_ident_country_code
|
||||
contact = Contact.new(ident_country_code: 'us')
|
||||
contact.validate
|
||||
assert_equal 'US', contact.ident_country_code
|
||||
def test_linked_scope_returns_contact_that_acts_as_admin_contact
|
||||
domains(:shop).admin_contacts = [@contact]
|
||||
assert Contact.linked.include?(@contact), 'Contact should be included'
|
||||
end
|
||||
|
||||
def test_generates_code
|
||||
contact = Contact.new(registrar: registrars(:bestnames))
|
||||
assert_nil contact.code
|
||||
|
||||
contact.generate_code
|
||||
|
||||
assert_not_empty contact.code
|
||||
def test_linked_scope_returns_contact_that_acts_as_tech_contact
|
||||
domains(:shop).tech_contacts = [@contact]
|
||||
assert Contact.linked.include?(@contact), 'Contact should be included'
|
||||
end
|
||||
|
||||
def test_prohibits_code_change
|
||||
assert_no_changes -> { @contact.code } do
|
||||
@contact.code = 'new'
|
||||
@contact.save!
|
||||
@contact.reload
|
||||
end
|
||||
def test_linked_scope_skips_unlinked_contact
|
||||
contact = unlinked_contact
|
||||
assert_not Contact.linked.include?(contact), 'Contact should be excluded'
|
||||
end
|
||||
|
||||
def test_removes_duplicate_statuses
|
||||
contact = Contact.new(statuses: %w[ok ok])
|
||||
assert_equal %w[ok], contact.statuses
|
||||
def test_unlinked_scope_returns_unlinked_contact
|
||||
contact = unlinked_contact
|
||||
assert Contact.unlinked.include?(contact), 'Contact should be included'
|
||||
end
|
||||
|
||||
def test_default_status
|
||||
contact = Contact.new
|
||||
assert_equal %w[ok], contact.statuses
|
||||
def test_unlinked_scope_skips_contact_that_is_linked_as_registrant
|
||||
contact = unlinked_contact
|
||||
domains(:shop).update_columns(registrant_id: contact.becomes(Registrant).id)
|
||||
|
||||
assert Contact.unlinked.exclude?(contact), 'Contact should be excluded'
|
||||
end
|
||||
|
||||
def test_unlinked_scope_skips_contact_that_is_linked_as_admin_contact
|
||||
contact = unlinked_contact
|
||||
domains(:shop).admin_contacts = [contact]
|
||||
|
||||
assert Contact.unlinked.exclude?(contact), 'Contact should be excluded'
|
||||
end
|
||||
|
||||
def test_unlinked_scope_skips_contact_that_is_linked_as_tech_contact
|
||||
contact = unlinked_contact
|
||||
domains(:shop).tech_contacts = [contact]
|
||||
|
||||
assert Contact.unlinked.exclude?(contact), 'Contact should be excluded'
|
||||
end
|
||||
|
||||
def test_whois_gets_updated_after_contact_save
|
||||
|
|
289
test/models/csync_record_test.rb
Normal file
289
test/models/csync_record_test.rb
Normal file
|
@ -0,0 +1,289 @@
|
|||
require 'test_helper'
|
||||
|
||||
class CsyncRecordTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@domain = domains(:shop)
|
||||
|
||||
@csync_record = CsyncRecord.new
|
||||
@csync_record.cdnskey = '257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ=='
|
||||
@csync_record.action = 'initialized'
|
||||
@csync_record.domain = domains(:shop)
|
||||
@csync_record.last_scan = Time.now
|
||||
end
|
||||
|
||||
def test_domain_must_be_present
|
||||
assert @csync_record.valid?
|
||||
@csync_record.domain = nil
|
||||
assert_not @csync_record.valid?
|
||||
end
|
||||
|
||||
def test_action_must_be_present_and_valid
|
||||
@csync_record.action = nil
|
||||
assert_not @csync_record.valid?
|
||||
|
||||
@csync_record.action = 'definitely invalid action'
|
||||
assert_not @csync_record.valid?
|
||||
|
||||
@csync_record.action = 'initialized'
|
||||
assert @csync_record.valid?
|
||||
|
||||
@csync_record.action = 'rollover'
|
||||
assert @csync_record.valid?
|
||||
|
||||
@csync_record.action = 'deactivate'
|
||||
assert_not @csync_record.valid?
|
||||
|
||||
@csync_record.action = 'deactivate'
|
||||
@csync_record.cdnskey = '0 3 0 AA=='
|
||||
assert @csync_record.valid?
|
||||
end
|
||||
|
||||
def test_cdnskey_must_be_unique_for_domain
|
||||
dnskey = @csync_record.dnskey
|
||||
dnskey.save!
|
||||
|
||||
assert_not @csync_record.valid?
|
||||
assert_includes @csync_record.errors.full_messages, 'Public key already tied to this domain'
|
||||
|
||||
@csync_record.cdnskey = nil
|
||||
assert_not @csync_record.valid?
|
||||
assert_includes @csync_record.errors.full_messages, 'Cdnskey is missing'
|
||||
end
|
||||
|
||||
def test_cdnskey_must_be_parsable
|
||||
@csync_record.cdnskey = 'gibberish'
|
||||
assert_not @csync_record.valid?
|
||||
|
||||
@csync_record.cdnskey = nil
|
||||
assert_not @csync_record.valid?
|
||||
@csync_record.cdnskey = ''
|
||||
assert_not @csync_record.valid?
|
||||
|
||||
@csync_record.cdnskey = '257 3 13 KlHFYV42UtxC7LpsolDpoUZ9DNPDRYQypalBRIqlubBg/zg78aqciLk+NaWUbrkN7AUaM7h7tx91sLN+ORVPxA=='
|
||||
assert @csync_record.valid?
|
||||
end
|
||||
|
||||
def test_initializes_valid_record_with_scanner_input
|
||||
scanner_result = {
|
||||
type: 'insecure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '257', proto: '3', alg: '13',
|
||||
pub: 'mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==',
|
||||
cdnskey: '257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ=='
|
||||
}
|
||||
|
||||
csync_record = CsyncRecord.by_domain_name(@domain.name)
|
||||
csync_record.assign_scanner_data!(scanner_result)
|
||||
|
||||
assert csync_record.valid?
|
||||
assert_equal scanner_result[:cdnskey], csync_record.cdnskey
|
||||
assert_equal 'initialized', csync_record.action
|
||||
assert_equal 1, csync_record.times_scanned
|
||||
end
|
||||
|
||||
def test_creates_rollover_record_with_scanner_input
|
||||
# Rollover always requires valid dnssec conf beforehand
|
||||
dnskey = @csync_record.dnskey
|
||||
dnskey.save!
|
||||
|
||||
# Type 'secure' reflects from cdnskey-scanner that custom domain security level is SECURE (dnssec valid)
|
||||
scanner_result = {
|
||||
type: 'secure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '256', proto: '3', alg: '13',
|
||||
pub: 'PiVTNvqOTrCSoXf5obNEPrDe0yhrKPmjyv+MWfoscBHF49rRIH1/yDdAXY3SyUD86qq/AiXDzsTQIqOjvak7gw==',
|
||||
cdnskey: '256 3 13 PiVTNvqOTrCSoXf5obNEPrDe0yhrKPmjyv+MWfoscBHF49rRIH1/yDdAXY3SyUD86qq/AiXDzsTQIqOjvak7gw=='
|
||||
}
|
||||
|
||||
csync_record = CsyncRecord.by_domain_name(@domain.name)
|
||||
csync_record.assign_scanner_data!(scanner_result)
|
||||
assert_equal 'rollover', csync_record.action
|
||||
assert csync_record.valid?
|
||||
|
||||
scanner_result[:type] = 'insecure'
|
||||
csync_record.assign_scanner_data!(scanner_result)
|
||||
assert_not csync_record.valid?
|
||||
assert_includes csync_record.errors.full_messages, 'Action is invalid'
|
||||
end
|
||||
|
||||
def test_creates_deactivate_record_with_scanner_input
|
||||
# Deactivate always requires valid dnssec conf beforehand
|
||||
dnskey = @csync_record.dnskey
|
||||
dnskey.save!
|
||||
|
||||
# Type 'secure' reflects from cdnskey-scanner that custom domain security level is SECURE (dnssec valid)
|
||||
scanner_result = {
|
||||
type: 'secure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '0', proto: '3', alg: '0',
|
||||
pub: 'AA==',
|
||||
cdnskey: '0 3 0 AA=='
|
||||
}
|
||||
|
||||
csync_record = CsyncRecord.by_domain_name(@domain.name)
|
||||
csync_record.assign_scanner_data!(scanner_result)
|
||||
assert_equal 'deactivate', csync_record.action
|
||||
assert csync_record.valid?
|
||||
|
||||
scanner_result[:type] = 'insecure'
|
||||
csync_record.assign_scanner_data!(scanner_result)
|
||||
assert_not csync_record.valid?
|
||||
assert_includes csync_record.errors.full_messages, 'Action is invalid'
|
||||
end
|
||||
|
||||
def test_initializes_dnssec_for_domain
|
||||
scanner_result = {
|
||||
type: 'insecure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '257', proto: '3', alg: '13',
|
||||
pub: 'mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==',
|
||||
cdnskey: '257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ=='
|
||||
}
|
||||
|
||||
2.times { CsyncRecord.by_domain_name(@domain.name).record_new_scan(scanner_result) }
|
||||
@domain.reload
|
||||
|
||||
CsyncRecord.stub :by_domain_name, @domain.csync_record do
|
||||
@domain.csync_record.stub :dnssec_validates?, true do
|
||||
CsyncRecord.by_domain_name(@domain.name).record_new_scan(scanner_result)
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 1, @domain.dnskeys.count
|
||||
assert_equal scanner_result[:pub], @domain.dnskeys.last.public_key
|
||||
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_equal (@domain.contacts.map(&:email) << @domain.registrant.email).uniq, mail.to
|
||||
assert_equal mail.subject, "Teie domeeni #{@domain.name} DNSSEC andmed on uuendatud / DNSSEC data for #{@domain.name} has been updated"
|
||||
end
|
||||
|
||||
def test_rollovers_dnssec_for_domain
|
||||
dnskey = @csync_record.dnskey
|
||||
dnskey.save!
|
||||
|
||||
scanner_result = {
|
||||
type: 'secure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '256', proto: '3', alg: '13',
|
||||
pub: 'PiVTNvqOTrCSoXf5obNEPrDe0yhrKPmjyv+MWfoscBHF49rRIH1/yDdAXY3SyUD86qq/AiXDzsTQIqOjvak7gw==',
|
||||
cdnskey: '256 3 13 PiVTNvqOTrCSoXf5obNEPrDe0yhrKPmjyv+MWfoscBHF49rRIH1/yDdAXY3SyUD86qq/AiXDzsTQIqOjvak7gw=='
|
||||
}
|
||||
|
||||
stub_any_instance(CsyncRecord, :dnssec_validates?, true) do
|
||||
CsyncRecord.by_domain_name(@domain.name).record_new_scan(scanner_result)
|
||||
end
|
||||
|
||||
assert_equal 1, @domain.dnskeys.count
|
||||
assert_equal scanner_result[:pub], @domain.dnskeys.last.public_key
|
||||
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_equal (@domain.contacts.map(&:email) << @domain.registrant.email).uniq, mail.to
|
||||
assert_equal mail.subject, "Teie domeeni #{@domain.name} DNSSEC andmed on uuendatud / DNSSEC data for #{@domain.name} has been updated"
|
||||
end
|
||||
|
||||
def test_deactivates_dnssec_for_domain
|
||||
dnskey = @csync_record.dnskey
|
||||
dnskey.save!
|
||||
|
||||
scanner_result = {
|
||||
type: 'secure', ns: 'ns1.bestnames.test', ns_ip: '127.0.0.1', flags: '0', proto: '3', alg: '0',
|
||||
pub: 'AA==',
|
||||
cdnskey: '0 3 0 AA=='
|
||||
}
|
||||
|
||||
stub_any_instance(CsyncRecord, :dnssec_validates?, true) do
|
||||
CsyncRecord.by_domain_name(@domain.name).record_new_scan(scanner_result)
|
||||
end
|
||||
|
||||
assert @domain.dnskeys.empty?
|
||||
|
||||
mail = ActionMailer::Base.deliveries.last
|
||||
assert_equal (@domain.contacts.map(&:email) << @domain.registrant.email).uniq, mail.to
|
||||
assert_equal mail.subject, "Teie domeeni #{@domain.name} DNSSEC andmed on eemaldatud / DNSSEC data for #{@domain.name} has been removed"
|
||||
end
|
||||
|
||||
def test_validates_security_level_for_initialize_action
|
||||
@csync_record.action = 'initialized'
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.INSECURE do
|
||||
assert @csync_record.valid_pre_action?
|
||||
assert_not @csync_record.valid_post_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.BOGUS do
|
||||
assert @csync_record.valid_pre_action?
|
||||
assert_not @csync_record.valid_post_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.SECURE do
|
||||
assert @csync_record.valid_post_action?
|
||||
assert_not @csync_record.valid_pre_action?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_security_level_for_rollover_action
|
||||
@csync_record.action = 'rollover'
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.INSECURE do
|
||||
assert_not @csync_record.valid_pre_action?
|
||||
assert_not @csync_record.valid_post_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.BOGUS do
|
||||
assert_not @csync_record.valid_pre_action?
|
||||
assert_not @csync_record.valid_post_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.SECURE do
|
||||
assert @csync_record.valid_pre_action?
|
||||
assert @csync_record.valid_post_action?
|
||||
end
|
||||
end
|
||||
|
||||
def test_validates_security_level_for_deactivate_action
|
||||
@csync_record.action = 'deactivate'
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.INSECURE do
|
||||
assert @csync_record.valid_post_action?
|
||||
assert_not @csync_record.valid_pre_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.BOGUS do
|
||||
assert @csync_record.valid_post_action?
|
||||
assert_not @csync_record.valid_pre_action?
|
||||
end
|
||||
|
||||
@domain.stub :dnssec_security_level, Dnsruby::Message::SecurityLevel.SECURE do
|
||||
assert @csync_record.valid_pre_action?
|
||||
assert_not @csync_record.valid_post_action?
|
||||
end
|
||||
end
|
||||
|
||||
def test_returns_correct_result_if_pre_and_post_actions_succeed
|
||||
stub_any_instance(CsyncRecord, :valid_post_action?, true) do
|
||||
stub_any_instance(CsyncRecord, :valid_pre_action?, true) do
|
||||
assert @csync_record.dnssec_validates?
|
||||
end
|
||||
end
|
||||
|
||||
stub_any_instance(CsyncRecord, :valid_post_action?, false) do
|
||||
stub_any_instance(CsyncRecord, :valid_pre_action?, false) do
|
||||
assert_not @csync_record.dnssec_validates?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_dnssec_validation_fails_if_domain_unreachable
|
||||
@domain.name = 'definitely.not.valid.domain.123'
|
||||
assert_not @csync_record.dnssec_validates?
|
||||
end
|
||||
|
||||
def stub_any_instance(klass, method, value)
|
||||
klass.class_eval do
|
||||
alias_method :"new_#{method}", method
|
||||
|
||||
define_method(method) do
|
||||
if value.respond_to?(:call)
|
||||
value.call
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
yield
|
||||
ensure
|
||||
klass.class_eval do
|
||||
undef_method method
|
||||
alias_method method, :"new_#{method}"
|
||||
undef_method :"new_#{method}"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue