diff --git a/app/controllers/admin/dns/zones_controller.rb b/app/controllers/admin/dns/zones_controller.rb index b38976283..10e432520 100644 --- a/app/controllers/admin/dns/zones_controller.rb +++ b/app/controllers/admin/dns/zones_controller.rb @@ -1,7 +1,7 @@ module Admin module DNS class ZonesController < AdminController - load_and_authorize_resource(class: DNS::Zone) + authorize_resource(class: 'DNS::Zone') before_action :load_zone, only: %i[edit update destroy] def index @@ -49,10 +49,21 @@ module Admin end def zone_params - params.require(:zone).permit( - :origin, :ttl, :refresh, :retry, :expire, :minimum_ttl, :email, - :master_nameserver, :ns_records, :a_records, :a4_records - ) + allowed_params = %i[ + origin + ttl + refresh + retry + expire + minimum_ttl + email + master_nameserver + ns_records + a_records + a4_records + ] + + params.require(:zone).permit(*allowed_params) end def redirect_to_index diff --git a/app/models/dns/zone.rb b/app/models/dns/zone.rb index e57c7b712..ac9c891f8 100644 --- a/app/models/dns/zone.rb +++ b/app/models/dns/zone.rb @@ -1,16 +1,13 @@ module DNS class Zone < ActiveRecord::Base + self.auto_html5_validation = false + validates :origin, :ttl, :refresh, :retry, :expire, :minimum_ttl, :email, :master_nameserver, presence: true validates :ttl, :refresh, :retry, :expire, :minimum_ttl, numericality: { only_integer: true } validates :origin, uniqueness: true - before_destroy :check_for_dependencies - - def check_for_dependencies - dc = Domain.where("name ILIKE ?", "%.#{origin}").count - return if dc == 0 - errors.add(:base, I18n.t('there_are_count_domains_in_this_zone', count: dc)) - false + before_destroy do + !used? end def self.generate_zonefiles @@ -37,6 +34,10 @@ module DNS pluck(:origin) end + def used? + Domain.uses_zone?(self) + end + def to_s origin end diff --git a/app/models/domain.rb b/app/models/domain.rb index 3e71dd1e9..259cf4267 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -724,5 +724,9 @@ class Domain < ActiveRecord::Base def self.delete_candidates where("#{attribute_alias(:delete_time)} < ?", Time.zone.now) end + + def self.uses_zone?(zone) + exists?(["name ILIKE ?", "%.#{zone.origin}"]) + end end # rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/dns/zones/_form.haml b/app/views/admin/dns/zones/_form.haml index b7f413156..4fc33a885 100644 --- a/app/views/admin/dns/zones/_form.haml +++ b/app/views/admin/dns/zones/_form.haml @@ -8,10 +8,10 @@ .col-md-4.control-label = f.label :origin .col-md-8 - - if zone.persisted? - = f.text_field :origin, class: 'form-control', disabled: true - - else + - if f.object.new_record? = f.text_field :origin, class: 'form-control', required: true + - else + = f.text_field :origin, class: 'form-control', disabled: true .form-group .col-md-4.control-label @@ -69,7 +69,7 @@ .form-group .col-md-4.control-label - = f.label :a4_records, t(:a4_records) + = f.label :a4_records .col-md-8 = f.text_area :a4_records, class: 'form-control', rows: 8 diff --git a/config/locales/en.yml b/config/locales/en.yml index 14c0d3188..b334c43a9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -884,8 +884,6 @@ en: result_count: '%{count} results' failed_to_generate_invoice_invoice_number_limit_reached: 'Failed to generate invoice - invoice number limit reached' is_too_small_minimum_deposit_is: 'is too small. Minimum deposit is %{amount} %{currency}' - a4_records: 'AAAA records' - there_are_count_domains_in_this_zone: 'There are %{count} domains in this zone' poll_pending_update_confirmed_by_registrant: 'Registrant confirmed domain update' poll_pending_update_rejected_by_registrant: 'Registrant rejected domain update' poll_pending_delete_rejected_by_registrant: 'Registrant rejected domain deletion' diff --git a/spec/models/dns/zone_spec.rb b/spec/models/dns/zone_spec.rb index 815ce4aca..a5d7861e6 100644 --- a/spec/models/dns/zone_spec.rb +++ b/spec/models/dns/zone_spec.rb @@ -10,4 +10,93 @@ RSpec.describe DNS::Zone do expect(described_class.origins).to eq('origins') end end + + describe 'validation' do + let(:zone) { described_class.new } + + required_attributes = %i[ + origin + ttl + refresh + retry + expire + minimum_ttl + email + master_nameserver + ] + + required_attributes.each do |attr_name| + it "rejects absent #{attr_name}", db: false do + zone.send("#{attr_name}=", nil) + zone.validate + expect(zone.errors).to have_key(attr_name) + end + end + + integer_attributes = %i[ + ttl + refresh + retry + expire + minimum_ttl + ] + + integer_attributes.each do |attr_name| + it "rejects non-integer #{attr_name}", db: false do + zone.send("#{attr_name}=", 'test') + zone.validate + expect(zone.errors).to have_key(attr_name) + end + + it "accepts integer #{attr_name}", db: false do + zone.send("#{attr_name}=", '1') + zone.validate + expect(zone.errors).to_not have_key(attr_name) + end + end + end + + describe '#used?', db: false do + let!(:zone) { described_class.new } + + context 'when domain uses zone' do + before :example do + allow(Domain).to receive(:uses_zone?).and_return(true) + end + + specify { expect(zone).to be_used } + end + + context 'when domain does not use zone' do + before :example do + allow(Domain).to receive(:uses_zone?).and_return(false) + end + + specify { expect(zone).to_not be_used } + end + end + + describe 'deletion', settings: false do + let!(:zone) { create(:zone) } + + context 'when zone is unused' do + before :example do + allow(zone).to receive(:used?).and_return(false) + end + + it 'is allowed' do + expect { zone.destroy }.to change { described_class.count }.from(1).to(0) + end + end + + context 'when zone is used' do + before :example do + allow(zone).to receive(:used?).and_return(true) + end + + it 'is disallowed' do + expect { zone.destroy }.to_not change { described_class.count } + end + end + end end diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index d0bad297c..12f267d38 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -837,6 +837,19 @@ RSpec.describe Domain, db: false do end end + describe '::uses_zone?', db: true do + let!(:zone) { create(:zone, origin: 'domain.tld') } + + context 'when zone is used' do + let!(:domain) { create(:domain, name: 'test.domain.tld') } + specify { expect(described_class.uses_zone?(zone)).to be true } + end + + context 'when zone is unused' do + specify { expect(described_class.uses_zone?(zone)).to be false } + end + end + describe '#new_registrant_email' do let(:domain) { described_class.new(pending_json: { new_registrant_email: 'test@test.com' }) } diff --git a/spec/requests/admin/dns/zones/create_spec.rb b/spec/requests/admin/dns/zones/create_spec.rb index 172e656dc..991bd60bc 100644 --- a/spec/requests/admin/dns/zones/create_spec.rb +++ b/spec/requests/admin/dns/zones/create_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'admin zone create', settings: false do .to change { DNS::Zone.count }.from(0).to(1) end - text_attributes = %i[origin email master_nameserver] + text_attributes = %i[origin email master_nameserver ns_records a_records a4_records] integer_attributes = %i[ttl refresh retry expire minimum_ttl] text_attributes.each do |attr_name| diff --git a/spec/requests/admin/dns/zones/update_spec.rb b/spec/requests/admin/dns/zones/update_spec.rb index 1e238e0cb..f9b25a3cf 100644 --- a/spec/requests/admin/dns/zones/update_spec.rb +++ b/spec/requests/admin/dns/zones/update_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'admin zone update', settings: false do sign_in_to_admin_area end - text_attributes = %i[origin email master_nameserver] + text_attributes = %i[origin email master_nameserver ns_records a_records a4_records] integer_attributes = %i[ttl refresh retry expire minimum_ttl] text_attributes.each do |attr_name|