diff --git a/app/models/domain.rb b/app/models/domain.rb index e69624d6e4..93e45e55b0 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -164,15 +164,35 @@ def status_is_consistant max: -> { Setting.dnskeys_max_count }, } - validates :admin_domain_contacts, object_count: { - min: -> { Setting.admin_contacts_min_count }, - max: -> { Setting.admin_contacts_max_count }, - } + def self.admin_contacts_validation_rules(for_org:) + { + min: -> { for_org ? Setting.admin_contacts_min_count : 0 }, + max: -> { Setting.admin_contacts_max_count } + } + end - validates :tech_domain_contacts, object_count: { - min: -> { Setting.tech_contacts_min_count }, - max: -> { Setting.tech_contacts_max_count }, - } + def self.tech_contacts_validation_rules(for_org:) + { + min: -> { for_org ? Setting.tech_contacts_min_count : 0 }, + max: -> { Setting.tech_contacts_max_count } + } + end + + validates :admin_domain_contacts, + object_count: admin_contacts_validation_rules(for_org: true), + if: :require_admin_contacts? + + validates :admin_domain_contacts, + object_count: admin_contacts_validation_rules(for_org: false), + unless: :require_admin_contacts? + + validates :tech_domain_contacts, + object_count: tech_contacts_validation_rules(for_org: true), + if: :require_tech_contacts? + + validates :tech_domain_contacts, + object_count: tech_contacts_validation_rules(for_org: false), + unless: :require_tech_contacts? validates :nameservers, uniqueness_multi: { attribute: 'hostname', @@ -835,4 +855,12 @@ def self.swap_elements(array, indexes) end array end + + def require_admin_contacts? + registrant.present? && registrant.org? + end + + def require_tech_contacts? + registrant.present? && registrant.org? + end end diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index bb2bc84a1f..343bba3bdc 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -114,7 +114,9 @@ def epp_code_map def attach_default_contacts return if registrant.blank? + registrant_obj = Contact.find_by(code: registrant.code) + return if registrant_obj.priv? tech_contacts << registrant_obj if tech_domain_contacts.blank? admin_contacts << registrant_obj if admin_domain_contacts.blank? && !registrant.org? diff --git a/test/integration/epp/domain/create/base_test.rb b/test/integration/epp/domain/create/base_test.rb index 180d165b35..71cfa1d422 100644 --- a/test/integration/epp/domain/create/base_test.rb +++ b/test/integration/epp/domain/create/base_test.rb @@ -436,6 +436,9 @@ def test_registers_new_domain_with_required_attributes contact = contacts(:john) registrant = contact.becomes(Registrant) + registrant.update!(ident_type: 'org') + registrant.reload + request_xml = <<-XML @@ -444,6 +447,7 @@ def test_registers_new_domain_with_required_attributes #{name} #{registrant.code} + #{contact.code} @@ -937,4 +941,54 @@ def test_returns_error_response_if_throttled ENV["shunter_default_threshold"] = '10000' ENV["shunter_enabled"] = 'false' end + + def test_registers_new_domain_with_private_registrant_without_admin_contacts + now = Time.zone.parse('2010-07-05') + travel_to now + name = "new.#{dns_zones(:one).origin}" + contact = contacts(:john) + registrant = contact.becomes(Registrant) + + registrant.update!(ident_type: 'priv') + registrant.reload + assert_not registrant.org? + + request_xml = <<-XML + + + + + + #{name} + #{registrant.code} + + + + + #{'test' * 2000} + + + + + XML + + assert_difference 'Domain.count' do + post epp_create_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end + response_xml = Nokogiri::XML(response.body) + assert_correct_against_schema response_xml + + assert_epp_response :completed_successfully + + domain = Domain.find_by(name: name) + assert_equal name, domain.name + assert_equal registrant, domain.registrant + assert_empty domain.admin_contacts + assert_empty domain.tech_contacts + assert_not_empty domain.transfer_code + + default_registration_period = 1.year + 1.day + assert_equal now + default_registration_period, domain.expire_time + end end diff --git a/test/integration/repp/v1/domains/contacts_test.rb b/test/integration/repp/v1/domains/contacts_test.rb index 87b9168118..a40cb3d145 100644 --- a/test/integration/repp/v1/domains/contacts_test.rb +++ b/test/integration/repp/v1/domains/contacts_test.rb @@ -108,9 +108,32 @@ def test_can_remove_tech_contacts refute @domain.tech_contacts.find_by(code: contact.code).present? end + def test_can_remove_all_admin_contacts_for_private_registrant + Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) + + @domain.registrant.update!(ident_type: 'priv') + @domain.reload + assert_not @domain.registrant.org? + + contact = @domain.admin_contacts.last + + payload = { contacts: [ { code: contact.code, type: 'admin' } ] } + delete "/repp/v1/domains/#{@domain.name}/contacts", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + @domain.reload + assert_response :ok + assert_equal 1000, json[:code] + + assert_empty @domain.admin_contacts + end + def test_can_not_remove_one_and_only_contact Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) + @domain.registrant.update!(ident_type: 'org') + @domain.reload + contact = @domain.admin_contacts.last payload = { contacts: [ { code: contact.code, type: 'admin' } ] } diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 57c3700894..b9c9056b26 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -221,6 +221,10 @@ def test_validates_admin_contact_count assert domain.valid?, proc { domain.errors.full_messages } domain.admin_domain_contacts.clear + + domain.registrant.update!(ident_type: 'org') + domain.reload + assert domain.registrant.org? assert domain.invalid? domain.admin_domain_contacts.clear @@ -236,6 +240,10 @@ def test_validates_tech_contact_count Setting.tech_contacts_min_count = min_count Setting.tech_contacts_max_count = max_count + domain.registrant.update!(ident_type: 'org') + domain.reload + assert domain.registrant.org? + domain.tech_domain_contacts.clear min_count.times { domain.tech_domain_contacts.build(domain_contact_attributes) } assert domain.valid?, proc { domain.errors.full_messages } @@ -475,6 +483,56 @@ def test_not_renewable_if_pending_delete_unconfirmed assert_not @domain.renewable? end + def test_validates_admin_contact_count_for_private_registrant + domain_contact_attributes = domain_contacts(:shop_jane).dup.attributes + domain = valid_domain + max_count = 2 + Setting.admin_contacts_max_count = max_count + + domain.registrant.update!(ident_type: 'priv') + domain.reload + assert_not domain.registrant.org? + + # Valid without any admin contacts + domain.admin_domain_contacts.clear + assert domain.valid?, proc { domain.errors.full_messages } + + # Valid with some admin contacts + domain.admin_domain_contacts.clear + max_count.pred.times { domain.admin_domain_contacts.build(domain_contact_attributes) } + assert domain.valid?, proc { domain.errors.full_messages } + + # Invalid when exceeding max contacts + domain.admin_domain_contacts.clear + max_count.next.times { domain.admin_domain_contacts.build(domain_contact_attributes) } + assert domain.invalid? + end + + def test_validates_tech_contact_count_for_private_registrant + domain_contact_attributes = domain_contacts(:shop_william).dup.attributes + domain = valid_domain + max_count = 2 + Setting.tech_contacts_max_count = max_count + + domain.registrant.update!(ident_type: 'priv') + domain.reload + assert_not domain.registrant.org? + + # Valid without any tech contacts + domain.tech_domain_contacts.clear + assert domain.valid?, proc { domain.errors.full_messages } + + # Valid with some tech contacts + domain.tech_domain_contacts.clear + max_count.pred.times { domain.tech_domain_contacts.build(domain_contact_attributes) } + assert domain.valid?, proc { domain.errors.full_messages } + + # Invalid when exceeding max contacts + domain.tech_domain_contacts.clear + max_count.next.times { domain.tech_domain_contacts.build(domain_contact_attributes) } + assert domain.invalid? + end + private def valid_domain