mirror of
https://github.com/internetee/registry.git
synced 2025-06-05 20:27:30 +02:00
141 lines
4.1 KiB
Ruby
141 lines
4.1 KiB
Ruby
# 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 dnskey_already_present?
|
|
|
|
errors.add(:public_key, 'already tied to this domain')
|
|
end
|
|
|
|
# since dnskeys stored in DB may include whitespace chars, we could not find them by
|
|
# 'where' clause using dnskey.public_key being stripped of whitespaces by csync generator
|
|
def dnskey_already_present?
|
|
domain.dnskeys.pluck(:public_key).map { |key| key.gsub(/\s+/, '') }.include? dnskey.public_key
|
|
end
|
|
|
|
def self.by_domain_name(domain_name)
|
|
domain = Domain.find_by(name: domain_name) || Domain.find_by(name_puny: 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
|
|
self.class.log
|
|
end
|
|
|
|
def self.log
|
|
Rails.env.test? ? logger : Logger.new(STDOUT)
|
|
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
|