diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f6065826..9afedbf17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +11.02.2021 +* Poll messages on locking and unlocking a domain [#1828](https://github.com/internetee/registry/issues/1828) +* Registrar's prefix is now checked and added to contact id for info and check requests [#1832](https://github.com/internetee/registry/issues/1832) + 10.02.2021 * Admin contact bulk change option for registrars [#1764](https://github.com/internetee/registry/issues/1764) * Option to remove email addresses from AWS SES Supression list [#1839](https://github.com/internetee/registry/issues/1839) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 85305213b..65354ff48 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -14,7 +14,7 @@ module Epp authorize! :check, Epp::Contact ids = params[:parsed_frame].css('id').map(&:text) - @results = Epp::Contact.check_availability(ids) + @results = Epp::Contact.check_availability(ids, reg: current_user.registrar.code) render_epp_response '/epp/contacts/check' end @@ -93,7 +93,11 @@ module Epp def find_contact code = params[:parsed_frame].css('id').text.strip.upcase - @contact = Epp::Contact.find_by!(code: code) + reg_code = current_user.registrar.code.upcase + arr = [code, "#{reg_code}:#{code}", "CID:#{code}", "CID:#{reg_code}:#{code}"] + + contact = arr.find { |c| Epp::Contact.find_by(code: c).present? } + @contact = Epp::Contact.find_by!(code: contact || code) end # diff --git a/app/models/concerns/domain/registry_lockable.rb b/app/models/concerns/domain/registry_lockable.rb index 4a759296d..2325e4b60 100644 --- a/app/models/concerns/domain/registry_lockable.rb +++ b/app/models/concerns/domain/registry_lockable.rb @@ -12,6 +12,7 @@ module Concerns statuses << DomainStatus::SERVER_DELETE_PROHIBITED statuses << DomainStatus::SERVER_TRANSFER_PROHIBITED self.locked_by_registrant_at = Time.zone.now + alert_registrar_lock_changes!(lock: true) save! end @@ -42,10 +43,21 @@ module Concerns statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) self.locked_by_registrant_at = nil + alert_registrar_lock_changes!(lock: false) save! end end + + def alert_registrar_lock_changes!(lock: true) + translation = lock ? 'locked' : 'unlocked' + registrar.notifications.create!( + text: I18n.t("notifications.texts.registrar_#{translation}", + domain_name: name), + attached_obj_id: name, + attached_obj_type: self.class.name + ) + end end end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 50ebac065..0c0ed3d5f 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -42,17 +42,12 @@ class Epp::Contact < Contact ) end - def check_availability(codes) + def check_availability(codes, reg:) codes = [codes] if codes.is_a?(String) - res = [] - codes.each do |x| - contact = find_by_epp_code(x) - if contact - res << { code: contact.code, avail: 0, reason: 'in use' } - else - res << { code: x, avail: 1 } - end + codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" }.map { |c| c.strip.upcase }.each do |x| + c = find_by_epp_code(x) + res << (c ? { code: c.code, avail: 0, reason: 'in use' } : { code: x, avail: 1 }) end res diff --git a/app/models/notification.rb b/app/models/notification.rb index e83b2c9da..07e824367 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -25,6 +25,10 @@ class Notification < ApplicationRecord '' end + def registry_lock? + text.include?('has been locked') || text.include?('has been unlocked') + end + private def set_defaults diff --git a/app/models/whois_record.rb b/app/models/whois_record.rb index 19805d583..9a6229fcb 100644 --- a/app/models/whois_record.rb +++ b/app/models/whois_record.rb @@ -97,7 +97,9 @@ class WhoisRecord < ApplicationRecord end def destroy_whois_record - Whois::Record.without_auctions.where(name: name).delete_all + return if Auction.find_by(domain: name).present? + + Whois::Record.where(name: name).delete_all end private diff --git a/app/views/epp/poll/poll_req.xml.builder b/app/views/epp/poll/poll_req.xml.builder index 664327dae..a58b082c5 100644 --- a/app/views/epp/poll/poll_req.xml.builder +++ b/app/views/epp/poll/poll_req.xml.builder @@ -15,12 +15,22 @@ xml.epp_head do end if @object end - if @notification.action&.contact - render(partial: 'epp/poll/action', - locals: { - builder: xml, - action: @notification.action - }) + if @notification.action&.contact || @notification.registry_lock? + if @notification.registry_lock? + state = @notification.text.include?('unlocked') ? 'unlock' : 'lock' + xml.extension do + xml.tag!('changePoll:changeData', + 'xmlns:changePoll': 'https://epp.tld.ee/schema/changePoll-1.0.xsd') do + xml.tag!('changePoll:operation', state) + end + end + else + render(partial: 'epp/poll/action', + locals: { + builder: xml, + action: @notification.action, + }) + end end render('epp/shared/trID', builder: xml) diff --git a/config/locales/notifications.en.yml b/config/locales/notifications.en.yml index 1dff4a97c..d67fb5a5b 100644 --- a/config/locales/notifications.en.yml +++ b/config/locales/notifications.en.yml @@ -6,3 +6,5 @@ en: It was associated with registrant %{old_registrant_code} and contacts %{old_contacts_codes}. contact_update: Contact %{contact} has been updated by registrant + registrar_locked: Domain %{domain_name} has been locked by registrant + registrar_unlocked: Domain %{domain_name} has been unlocked by registrant diff --git a/doc/repp/v1/admin_contacts.md b/doc/repp/v1/admin_contacts.md new file mode 100644 index 000000000..3b78829d0 --- /dev/null +++ b/doc/repp/v1/admin_contacts.md @@ -0,0 +1,30 @@ +# Admin domain contacts + +## PATCH https://repp.internet.ee/v1/domains/admin_contacts +Replaces admin domain contacts of the current registrar. + +### Example request +``` +PATCH /repp/v1/domains/admin_contacts HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Basic dGVzdDp0ZXN0dGVzdA== + +{ + "current_contact_id": "ATSAA:749AA80F", + "new_contact_id": "ATSAA:E36957D7" +} +``` +### Example response +``` +{ + "code": 1000, + "message": "Command completed successfully", + "data": { + "affected_domains": [ + "private.ee", + ], + "skipped_domains": [] + } +} +``` diff --git a/doc/repp/v1/domain_contacts.md b/doc/repp/v1/domain_contacts.md index 2e542bf81..8c3c9b9de 100644 --- a/doc/repp/v1/domain_contacts.md +++ b/doc/repp/v1/domain_contacts.md @@ -1,7 +1,7 @@ -# Domain contacts +# Tech domain contacts ## PATCH https://repp.internet.ee/v1/domains/contacts -Replaces all domain contacts of the current registrar. +Replaces technical domain contacts of the current registrar. ### Example request ``` diff --git a/test/integration/epp/contact/check/base_test.rb b/test/integration/epp/contact/check/base_test.rb index 528d69d86..8f7e3df16 100644 --- a/test/integration/epp/contact/check/base_test.rb +++ b/test/integration/epp/contact/check/base_test.rb @@ -26,7 +26,7 @@ class EppContactCheckBaseTest < EppTestCase response_xml = Nokogiri::XML(response.body) assert_epp_response :completed_successfully - assert_equal 'john-001', response_xml.at_xpath('//contact:id', contact: xml_schema).text + assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text end def test_contact_is_available @@ -52,7 +52,8 @@ class EppContactCheckBaseTest < EppTestCase end def test_contact_is_unavailable - assert_equal 'john-001', @contact.code + @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase) + assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase request_xml = <<-XML @@ -98,9 +99,61 @@ class EppContactCheckBaseTest < EppTestCase assert_equal 3, response_xml.xpath('//contact:cd', contact: xml_schema).size end + def test_check_contact_with_prefix + @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase) + assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase + + request_xml = <<-XML + + + + + + BESTNAMES:JOHN-001 + + + + + XML + + post epp_check_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + + response_xml = Nokogiri::XML(response.body) + assert_epp_response :completed_successfully + assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text + assert_equal 'in use', response_xml.at_xpath('//contact:reason', contact: xml_schema).text + end + + def test_check_contact_without_prefix + @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase) + assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase + + request_xml = <<-XML + + + + + + JOHN-001 + + + + + XML + + post epp_check_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + + response_xml = Nokogiri::XML(response.body) + assert_epp_response :completed_successfully + assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text + assert_equal 'in use', response_xml.at_xpath('//contact:reason', contact: xml_schema).text + end + private def xml_schema 'https://epp.tld.ee/schema/contact-ee-1.1.xsd' end -end \ No newline at end of file +end diff --git a/test/integration/epp/contact/info/base_test.rb b/test/integration/epp/contact/info/base_test.rb index 4e4a9190e..3edb6d461 100644 --- a/test/integration/epp/contact/info/base_test.rb +++ b/test/integration/epp/contact/info/base_test.rb @@ -44,6 +44,58 @@ class EppContactInfoBaseTest < EppTestCase contact: xml_schema).text end + def test_get_info_about_contact_with_prefix + @contact.update_columns(code: 'TEST:JOHN-001') + assert @contact.code, 'TEST:JOHN-001' + + request_xml = <<-XML + + + + + + TEST:JOHN-001 + + + + + XML + + post epp_info_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + + response_xml = Nokogiri::XML(response.body) + assert_epp_response :completed_successfully + assert_equal 'TEST:JOHN-001', response_xml.at_xpath('//contact:id', contact: xml_schema).text + assert_equal '+555.555', response_xml.at_xpath('//contact:voice', contact: xml_schema).text + end + + def test_get_info_about_contact_without_prefix + @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase) + assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase + + request_xml = <<-XML + + + + + + JOHN-001 + + + + + XML + + post epp_info_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + + response_xml = Nokogiri::XML(response.body) + assert_epp_response :completed_successfully + assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text + assert_equal '+555.555', response_xml.at_xpath('//contact:voice', contact: xml_schema).text + end + def test_hides_password_and_name_when_current_registrar_is_not_sponsoring non_sponsoring_registrar = registrars(:goodnames) @contact.update!(registrar: non_sponsoring_registrar) diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index ded0d3d8a..d2d3a3252 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -1,5 +1,4 @@ require "test_helper" - class DomainUpdateConfirmJobTest < ActiveSupport::TestCase def setup super @@ -19,6 +18,22 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase super end + def test_registrant_locked_domain + refute @domain.locked_by_registrant? + @domain.apply_registry_lock + assert @domain.locked_by_registrant? + assert_equal(@domain.registrar.notifications.last.text, "Domain #{@domain.name} has been locked by registrant") + end + + def test_registrant_unlocked_domain + refute @domain.locked_by_registrant? + @domain.apply_registry_lock + assert @domain.locked_by_registrant? + @domain.remove_registry_lock + refute @domain.locked_by_registrant? + assert_equal(@domain.registrar.notifications.last.text, "Domain #{@domain.name} has been unlocked by registrant") + end + def test_rejected_registrant_verification_notifies_registrar DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED)