From a13b336f54c5fa73e3b40183a02e57e9d57b17f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 5 Oct 2020 14:44:48 +0300 Subject: [PATCH 001/172] Allow renewal regardless of pendingDelete status flag --- app/models/domain.rb | 4 ++-- app/models/epp/domain.rb | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index 679669728..9a8441eb5 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -320,8 +320,8 @@ class Domain < ApplicationRecord def renew_blocking_statuses disallowed = [DomainStatus::DELETE_CANDIDATE, DomainStatus::PENDING_RENEW, DomainStatus::PENDING_TRANSFER, DomainStatus::CLIENT_RENEW_PROHIBITED, - DomainStatus::PENDING_UPDATE, DomainStatus::PENDING_DELETE, - DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::SERVER_RENEW_PROHIBITED] + DomainStatus::PENDING_UPDATE, DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::PENDING_DELETE_CONFIRMATION] (statuses & disallowed) end diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index 530e54a0f..c46732712 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -612,6 +612,7 @@ class Epp::Domain < Domain statuses.delete(DomainStatus::SERVER_HOLD) statuses.delete(DomainStatus::EXPIRED) statuses.delete(DomainStatus::SERVER_UPDATE_PROHIBITED) + cancel_pending_delete save end From d81fd763e28b37e87cb523f372ae9e17b01c937b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 5 Oct 2020 15:33:38 +0300 Subject: [PATCH 002/172] Test domain is renewable if pendingDelete is set --- test/models/domain_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index a943be6ef..4a9240f57 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -444,6 +444,20 @@ class DomainTest < ActiveSupport::TestCase assert_not @domain.renewable? end + def test_renewable_if_pending_delete + assert @domain.renewable? + @domain.statuses << DomainStatus::PENDING_DELETE + + assert @domain.renewable? + end + + def test_not_renewable_if_pending_delete_unconfirmed + assert @domain.renewable? + @domain.statuses << DomainStatus::PENDING_DELETE_CONFIRMATION + + assert_not @domain.renewable? + end + private def valid_domain From a33cc40d59567fc0f659d0e8b6a330bb1b2009e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 5 Oct 2020 17:34:36 +0300 Subject: [PATCH 003/172] Extract Contact create BL from EPP controller --- app/controllers/epp/contacts_controller.rb | 12 +++--- app/models/actions/contact_create.rb | 44 ++++++++++++++++++++++ lib/deserializers/xml/contact_create.rb | 23 +++++++++++ 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 app/models/actions/contact_create.rb create mode 100644 lib/deserializers/xml/contact_create.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index df9755af6..5b2323919 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,5 +1,5 @@ require 'deserializers/xml/contact_update' - +require 'deserializers/xml/contact_create' module Epp class ContactsController < BaseController before_action :find_contact, only: [:info, :update, :delete] @@ -21,13 +21,13 @@ module Epp def create authorize! :create, Epp::Contact - frame = params[:parsed_frame] - @contact = Epp::Contact.new(frame, current_user.registrar) - @contact.add_legal_file_to_new(frame) - @contact.generate_code + @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) + collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - if @contact.save + action = Actions::ContactCreate.new(@contact, collected_data.legal_document) + + if action.call if !address_processing? && address_given? @response_code = 1100 @response_description = t('epp.contacts.completed_without_address') diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb new file mode 100644 index 000000000..a79876f4e --- /dev/null +++ b/app/models/actions/contact_create.rb @@ -0,0 +1,44 @@ +module Actions + class ContactCreate + attr_reader :contact, :legal_document + + def initialize(contact, legal_document) + @contact = contact + @legal_document = legal_document + end + + def call + maybe_remove_address + maybe_attach_legal_doc + 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 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.generate_code + + contact.save + end + end +end diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb new file mode 100644 index 000000000..7f5cb3709 --- /dev/null +++ b/lib/deserializers/xml/contact_create.rb @@ -0,0 +1,23 @@ +require 'deserializers/xml/legal_document' +require 'deserializers/xml/ident' +require 'deserializers/xml/contact' + +module Deserializers + module Xml + class ContactCreate + attr_reader :frame + + def initialize(frame) + @frame = frame + end + + def contact + @contact ||= ::Deserializers::Xml::Contact.new(frame).call + end + + def legal_document + @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call + end + end + end +end From f72ec0ec3ad6d4e96d6adc76cbabbe8cd964a40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 6 Oct 2020 14:28:57 +0300 Subject: [PATCH 004/172] Add contact create to REPP --- app/api/repp/contact_v1.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 810829ef7..1eb5a88ce 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -30,6 +30,37 @@ module Repp total_number_of_records: current_user.registrar.contacts.count } end + + desc 'Create new contact object' + params do + requires :contact, type: Hash, allow_blank: false do + requires :name, type: String, desc: 'Full name of contact' + requires :ident, type: String, desc: 'Government identifier of contact' + requires :ident_type, type: String, desc: 'Type of contact ident' + requires :ident_country_code, type: String, desc: 'Ident country code' + requires :country_code, type: String, desc: 'Address country' + requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + requires :email, type: String, desc: 'Email address of contact' + requires :fax, type: String, desc: 'Fax number of contact' + requires :street, type: String, desc: 'Address street' + requires :city, type: String, desc: 'Address city' + requires :zip, type: String, desc: 'Address ZIP' + end + end + + post '/' do + @legal_doc = params[:legal_documents] + @contact = Contact.new(params[:contact]) + @contact.registrar = current_user.registrar + action = Actions::ContactCreate.new(@contact, @legal_doc) + + if action.call + @response = { data: { contact: { id: @contact.id } } } + else + status :bad_request + @response = { errors: @contact.errors } + end + end end end end From d4628d52baff1c0b4f3fc68ad00fe9387b803582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 6 Oct 2020 16:02:13 +0300 Subject: [PATCH 005/172] Add basic contact update functionality to REPP --- app/api/repp/contact_v1.rb | 58 ++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 1eb5a88ce..cdcc3ff6f 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -31,7 +31,7 @@ module Repp } end - desc 'Create new contact object' + desc 'Creates a new contact object' params do requires :contact, type: Hash, allow_blank: false do requires :name, type: String, desc: 'Full name of contact' @@ -41,10 +41,14 @@ module Repp requires :country_code, type: String, desc: 'Address country' requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' - requires :fax, type: String, desc: 'Fax number of contact' - requires :street, type: String, desc: 'Address street' - requires :city, type: String, desc: 'Address city' - requires :zip, type: String, desc: 'Address ZIP' + optional :fax, type: String, desc: 'Fax number of contact' + optional :street, type: String, desc: 'Address street' + optional :city, type: String, desc: 'Address city' + optional :zip, type: String, desc: 'Address ZIP' + end + optional :legal_document, type: Hash, allow_blank: false do + requires :body, type: String, desc: 'Raw data of legal document' + requires :type, type: String, desc: 'Format of legal document' end end @@ -55,7 +59,49 @@ module Repp action = Actions::ContactCreate.new(@contact, @legal_doc) if action.call - @response = { data: { contact: { id: @contact.id } } } + @response = { contact: { id: @contact.code } } + else + status :bad_request + @response = { errors: @contact.errors } + end + end + + desc 'Update contact properties' + params do + requires :contact, type: Hash, allow_blank: false do + optional :ident, type: Hash, allow_blank: false do + requires :ident, type: String, desc: 'Government identifier of contact' + requires :ident_type, type: String, desc: 'Type of contact ident' + requires :ident_country_code, type: String, desc: 'Ident country code' + end + optional :name, type: String, desc: 'Full name of contact' + optional :country_code, type: String, desc: 'Address country' + optional :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + optional :email, type: String, desc: 'Email address of contact' + optional :fax, type: String, desc: 'Fax number of contact' + optional :street, type: String, desc: 'Address street' + optional :city, type: String, desc: 'Address city' + optional :zip, type: String, desc: 'Address ZIP' + end + optional :legal_document, type: Hash, allow_blank: false do + requires :body, type: String, desc: 'Raw data of legal document' + requires :type, type: String, desc: 'Format of legal document' + end + end + + post '/:code' do + @contact = current_user.registrar.contacts.find_by(code: params[:code]) + (status(:not_found) && return) unless @contact + + @new_params = params[:contact] + @legal_doc = params[:legal_document] + @ident = params[:contact][:ident] || {} + + action = Actions::ContactUpdate.new(@contact, @new_params, + @legal_doc, @ident, current_user) + + if action.call + @response = {} else status :bad_request @response = { errors: @contact.errors } From 01004b8ed4ee1458f918349d7043ba9cac82b80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 7 Oct 2020 12:46:19 +0300 Subject: [PATCH 006/172] Repp: Contact create: validate identifier --- app/api/repp/contact_v1.rb | 46 ++++++++++++++++------ app/controllers/epp/contacts_controller.rb | 2 +- app/models/actions/contact_create.rb | 27 +++++++++++-- app/models/epp/contact.rb | 7 ++-- lib/deserializers/xml/contact_create.rb | 4 ++ 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index cdcc3ff6f..6a3cae33b 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -34,18 +34,29 @@ module Repp desc 'Creates a new contact object' params do requires :contact, type: Hash, allow_blank: false do + # Contact info requires :name, type: String, desc: 'Full name of contact' - requires :ident, type: String, desc: 'Government identifier of contact' - requires :ident_type, type: String, desc: 'Type of contact ident' - requires :ident_country_code, type: String, desc: 'Ident country code' - requires :country_code, type: String, desc: 'Address country' requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, desc: 'Fax number of contact' - optional :street, type: String, desc: 'Address street' - optional :city, type: String, desc: 'Address city' - optional :zip, type: String, desc: 'Address ZIP' + optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' + + # Ident + requires :ident, type: Hash do + requires :ident, type: String, allow_blank: false, desc: 'Government identifier of contact' + requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' + requires :ident_country_code, type: String, allow_blank: false, desc: 'Ident country code' + end + + # Physical address + optional :addr, type: Hash do + requires :country_code, type: String, allow_blank: false, desc: 'Address country' + requires :street, type: String, allow_blank: false, desc: 'Address street' + requires :city, type: String, allow_blank: false, desc: 'Address city' + requires :zip, type: String, allow_blank: false, desc: 'Address ZIP' + end end + + # Legal document optional :legal_document, type: Hash, allow_blank: false do requires :body, type: String, desc: 'Raw data of legal document' requires :type, type: String, desc: 'Format of legal document' @@ -54,9 +65,20 @@ module Repp post '/' do @legal_doc = params[:legal_documents] - @contact = Contact.new(params[:contact]) - @contact.registrar = current_user.registrar - action = Actions::ContactCreate.new(@contact, @legal_doc) + @contact_params = params[:contact] + + # Ident object + @ident = @contact_params[:ident] + @contact_params.delete(:ident) + + # Address + %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } + @contact_params.delete(:addr) + + @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + puts "#{params[:contact]}" + + action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) if action.call @response = { contact: { id: @contact.code } } @@ -89,7 +111,7 @@ module Repp end end - post '/:code' do + put '/:code' do @contact = current_user.registrar.contacts.find_by(code: params[:code]) (status(:not_found) && return) unless @contact diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 5b2323919..cfaaeb89e 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -25,7 +25,7 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document) + action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) if action.call if !address_processing? && address_given? diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index a79876f4e..4cb6607ae 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -1,15 +1,17 @@ module Actions class ContactCreate - attr_reader :contact, :legal_document + attr_reader :contact, :legal_document, :ident - def initialize(contact, legal_document) + 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 @@ -23,6 +25,24 @@ module Actions contact.country_code = nil end + def validate_ident + if ident.present? && ident[:ident_type].blank? + contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + end + + if ident.present? && 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 + + identifier = ::Contact::Ident.new(code: ident[:ident], type: ident[:ident_type], + country_code: ident[:ident_country_code]) + + identifier.validate + contact.identifier = identifier + end + def maybe_attach_legal_doc return unless legal_document @@ -36,8 +56,9 @@ module Actions end def commit - contact.generate_code + return false if @error + contact.generate_code contact.save end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 6867b037d..50ebac065 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -30,12 +30,13 @@ class Epp::Contact < Contact at end - def new(frame, registrar) + def new(frame, registrar, epp: true) return super if frame.blank? + attrs = epp ? attrs_from(frame, new_record: true) : frame super( - attrs_from(frame, new_record: true).merge( - code: frame.css('id').text, + attrs.merge( + code: epp ? frame.css('id').text : frame[:id], registrar: registrar ) ) diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb index 7f5cb3709..5bd9c768a 100644 --- a/lib/deserializers/xml/contact_create.rb +++ b/lib/deserializers/xml/contact_create.rb @@ -18,6 +18,10 @@ module Deserializers def legal_document @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call end + + def ident + @ident ||= ::Deserializers::Xml::Ident.new(frame).call + end end end end From dc8551807a24f6d4d6550c8933e60206c9f4e76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 7 Oct 2020 14:17:53 +0300 Subject: [PATCH 007/172] Add error structuring to Grape API --- app/api/repp/api.rb | 8 ++++++ app/api/repp/contact_v1.rb | 31 ++++++++++++++++------ app/controllers/epp/contacts_controller.rb | 1 - 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb index af6864cfa..f1908f81e 100644 --- a/app/api/repp/api.rb +++ b/app/api/repp/api.rb @@ -3,6 +3,14 @@ module Repp format :json prefix :repp + rescue_from Grape::Exceptions::ValidationErrors do |e| + messages = e.full_messages + errors = [] + messages.each { |m| errors << { code: 2003, message: m } } + + error!({ errors: errors }, 400) + end + http_basic do |username, password| @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) if @current_user diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 6a3cae33b..85065f5a0 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -36,15 +36,18 @@ module Repp requires :contact, type: Hash, allow_blank: false do # Contact info requires :name, type: String, desc: 'Full name of contact' - requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + requires :phone, type: String, + desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' # Ident requires :ident, type: Hash do - requires :ident, type: String, allow_blank: false, desc: 'Government identifier of contact' + requires :ident, type: String, allow_blank: false, + desc: 'Government identifier of contact' requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' - requires :ident_country_code, type: String, allow_blank: false, desc: 'Ident country code' + requires :ident_country_code, type: String, allow_blank: false, + desc: 'Ident country code' end # Physical address @@ -72,18 +75,29 @@ module Repp @contact_params.delete(:ident) # Address + address_present = params[:contact][:addr].keys.any? + %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } @contact_params.delete(:addr) @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) - puts "#{params[:contact]}" action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) if action.call - @response = { contact: { id: @contact.code } } + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + @response = { code: @response_code, + description: @response_description, + data: { contact: { id: @contact.code } } } else - status :bad_request + status(:bad_request) @response = { errors: @contact.errors } end end @@ -98,7 +112,8 @@ module Repp end optional :name, type: String, desc: 'Full name of contact' optional :country_code, type: String, desc: 'Address country' - optional :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + optional :phone, type: String, + desc: 'Phone number of contact. In format of +country_prefix.number' optional :email, type: String, desc: 'Email address of contact' optional :fax, type: String, desc: 'Fax number of contact' optional :street, type: String, desc: 'Address street' @@ -125,7 +140,7 @@ module Repp if action.call @response = {} else - status :bad_request + status(:bad_request) @response = { errors: @contact.errors } end end diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index cfaaeb89e..b674d0919 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -24,7 +24,6 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) if action.call From 9b3559c8b811ac1748a9bb921f173296bbccca72 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 7 Oct 2020 16:10:55 +0500 Subject: [PATCH 008/172] Add data migration to i18ze disclaimer --- ...01007104651_make_whois_disclamer_i18ned.rb | 21 +++++++++++++++++++ db/data_schema.rb | 2 +- test/fixtures/setting_entries.yml | 5 ++--- test/models/whois/record_test.rb | 12 +++++------ test/models/whois_record_test.rb | 5 +---- 5 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 db/data/20201007104651_make_whois_disclamer_i18ned.rb diff --git a/db/data/20201007104651_make_whois_disclamer_i18ned.rb b/db/data/20201007104651_make_whois_disclamer_i18ned.rb new file mode 100644 index 000000000..59bec04d8 --- /dev/null +++ b/db/data/20201007104651_make_whois_disclamer_i18ned.rb @@ -0,0 +1,21 @@ +class MakeWhoisDisclamerI18ned < ActiveRecord::Migration[6.0] + def up + entry = SettingEntry.find_by(code: 'registry_whois_disclaimer') + hash = { en: 'Search results may not be used for commercial, advertising, recompilation, repackaging, redistribution, reuse, obscuring or other similar activities.', + et: 'Otsitulemusi ei tohi kasutada ärilistel, reklaami, ümber töötlemise, edasi levitamise, taaskasutuse, muutmise ega muul sarnasel eesmärgil.', + ru: 'Результаты поиска не могут быть использованы в коммерческих целях, включая, но не ограничиваясь, рекламу, рекомпиляцию, изменение формата, перераспределение либо переиспользование.' } + string = JSON.generate(hash) + entry.format = 'hash' + entry.value = string + entry.save! + end + + def down + entry = SettingEntry.find_by(code: 'registry_whois_disclaimer') + string = 'Search results may not be used for commercial, advertising, recompilation, \ + repackaging, redistribution, reuse, obscuring or other similar activities.' + entry.format = 'string' + entry.value = string + entry.save! + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index 2968b1184..84cdf8411 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1,2 +1,2 @@ # encoding: UTF-8 -DataMigrate::Data.define(version: 20200901131427) +DataMigrate::Data.define(version: 20201007104651) diff --git a/test/fixtures/setting_entries.yml b/test/fixtures/setting_entries.yml index 78db36465..7ac1dd611 100644 --- a/test/fixtures/setting_entries.yml +++ b/test/fixtures/setting_entries.yml @@ -448,10 +448,9 @@ dispute_period_in_months: registry_whois_disclaimer: code: registry_whois_disclaimer - value: 'Search results may not be used for commercial, advertising, recompilation, - repackaging, redistribution, reuse, obscuring or other similar activities.' + value: "{\"en\":\"111\",\"et\":\"222\",\"ru\":\"333\"}" group: contacts - format: string + format: hash created_at: <%= Time.zone.parse('2010-07-05') %> updated_at: <%= Time.zone.parse('2010-07-05') %> diff --git a/test/models/whois/record_test.rb b/test/models/whois/record_test.rb index 3e727d80a..e900a4965 100644 --- a/test/models/whois/record_test.rb +++ b/test/models/whois/record_test.rb @@ -8,7 +8,7 @@ class Whois::RecordTest < ActiveSupport::TestCase @auction = auctions(:one) @original_disclaimer = Setting.registry_whois_disclaimer - Setting.registry_whois_disclaimer = 'disclaimer' + Setting.registry_whois_disclaimer = JSON.generate({en: 'disclaimer'}) end teardown do @@ -16,8 +16,8 @@ class Whois::RecordTest < ActiveSupport::TestCase end def test_reads_disclaimer_setting - Setting.registry_whois_disclaimer = 'test disclaimer' - assert_equal 'test disclaimer', Whois::Record.disclaimer + Setting.registry_whois_disclaimer = JSON.generate({en: 'test_disclaimer'}) + assert_equal Setting.registry_whois_disclaimer, Whois::Record.disclaimer end def test_updates_whois_record_from_auction_when_started @@ -28,7 +28,7 @@ class Whois::RecordTest < ActiveSupport::TestCase assert_equal ({ 'name' => 'domain.test', 'status' => ['AtAuction'], - 'disclaimer' => 'disclaimer' }), @whois_record.json + 'disclaimer' => { 'en' => 'disclaimer' }}), @whois_record.json end def test_updates_whois_record_from_auction_when_no_bids @@ -49,7 +49,7 @@ class Whois::RecordTest < ActiveSupport::TestCase assert_equal ({ 'name' => 'domain.test', 'status' => ['PendingRegistration'], - 'disclaimer' => 'disclaimer', + 'disclaimer' => { 'en' => 'disclaimer' }, 'registration_deadline' => registration_deadline.try(:to_s, :iso8601) }), @whois_record.json end @@ -64,7 +64,7 @@ class Whois::RecordTest < ActiveSupport::TestCase assert_equal ({ 'name' => 'domain.test', 'status' => ['PendingRegistration'], - 'disclaimer' => 'disclaimer', + 'disclaimer' => { 'en' => 'disclaimer' }, 'registration_deadline' => registration_deadline.try(:to_s, :iso8601) }), @whois_record.json end diff --git a/test/models/whois_record_test.rb b/test/models/whois_record_test.rb index 63e19d2be..cacd0e0fa 100644 --- a/test/models/whois_record_test.rb +++ b/test/models/whois_record_test.rb @@ -12,10 +12,7 @@ class WhoisRecordTest < ActiveSupport::TestCase end def test_generated_json_has_expected_values - expected_disclaimer_text = <<-TEXT.squish - Search results may not be used for commercial, advertising, recompilation, - repackaging, redistribution, reuse, obscuring or other similar activities. - TEXT + expected_disclaimer_text = SettingEntry.find_by(code: 'registry_whois_disclaimer').retrieve expected_partial_hash = { disclaimer: expected_disclaimer_text, From aa325604f9673f18aa2d92803723807bc8c93943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 8 Oct 2020 15:54:23 +0300 Subject: [PATCH 009/172] Port REPP Contacts from Grape to Rails internal API --- app/api/repp/contact_v1.rb | 149 ------------------ app/controllers/repp/v1/base_controller.rb | 82 ++++++++++ .../repp/v1/contacts_controller.rb | 111 +++++++++++++ config/routes.rb | 3 +- 4 files changed, 194 insertions(+), 151 deletions(-) delete mode 100644 app/api/repp/contact_v1.rb create mode 100644 app/controllers/repp/v1/base_controller.rb create mode 100644 app/controllers/repp/v1/contacts_controller.rb diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb deleted file mode 100644 index 85065f5a0..000000000 --- a/app/api/repp/contact_v1.rb +++ /dev/null @@ -1,149 +0,0 @@ -module Repp - class ContactV1 < Grape::API - version 'v1', using: :path - - resource :contacts do - desc 'Return list of contact' - params do - optional :limit, type: Integer, values: (1..200).to_a, desc: 'How many contacts to show' - optional :offset, type: Integer, desc: 'Contact number to start at' - optional :details, type: String, values: %w(true false), desc: 'Whether to include details' - end - - get '/' do - limit = params[:limit] || 200 - offset = params[:offset] || 0 - - if params[:details] == 'true' - contacts = current_user.registrar.contacts.limit(limit).offset(offset) - - unless Contact.address_processing? - attributes = Contact.attribute_names - Contact.address_attribute_names - contacts = contacts.select(attributes) - end - else - contacts = current_user.registrar.contacts.limit(limit).offset(offset).pluck(:code) - end - - @response = { - contacts: contacts, - total_number_of_records: current_user.registrar.contacts.count - } - end - - desc 'Creates a new contact object' - params do - requires :contact, type: Hash, allow_blank: false do - # Contact info - requires :name, type: String, desc: 'Full name of contact' - requires :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - requires :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' - - # Ident - requires :ident, type: Hash do - requires :ident, type: String, allow_blank: false, - desc: 'Government identifier of contact' - requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' - requires :ident_country_code, type: String, allow_blank: false, - desc: 'Ident country code' - end - - # Physical address - optional :addr, type: Hash do - requires :country_code, type: String, allow_blank: false, desc: 'Address country' - requires :street, type: String, allow_blank: false, desc: 'Address street' - requires :city, type: String, allow_blank: false, desc: 'Address city' - requires :zip, type: String, allow_blank: false, desc: 'Address ZIP' - end - end - - # Legal document - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - post '/' do - @legal_doc = params[:legal_documents] - @contact_params = params[:contact] - - # Ident object - @ident = @contact_params[:ident] - @contact_params.delete(:ident) - - # Address - address_present = params[:contact][:addr].keys.any? - - %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } - @contact_params.delete(:addr) - - @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) - - action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) - - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end - - @response = { code: @response_code, - description: @response_description, - data: { contact: { id: @contact.code } } } - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - - desc 'Update contact properties' - params do - requires :contact, type: Hash, allow_blank: false do - optional :ident, type: Hash, allow_blank: false do - requires :ident, type: String, desc: 'Government identifier of contact' - requires :ident_type, type: String, desc: 'Type of contact ident' - requires :ident_country_code, type: String, desc: 'Ident country code' - end - optional :name, type: String, desc: 'Full name of contact' - optional :country_code, type: String, desc: 'Address country' - optional :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - optional :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, desc: 'Fax number of contact' - optional :street, type: String, desc: 'Address street' - optional :city, type: String, desc: 'Address city' - optional :zip, type: String, desc: 'Address ZIP' - end - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - put '/:code' do - @contact = current_user.registrar.contacts.find_by(code: params[:code]) - (status(:not_found) && return) unless @contact - - @new_params = params[:contact] - @legal_doc = params[:legal_document] - @ident = params[:contact][:ident] || {} - - action = Actions::ContactUpdate.new(@contact, @new_params, - @legal_doc, @ident, current_user) - - if action.call - @response = {} - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb new file mode 100644 index 000000000..30e37b9f7 --- /dev/null +++ b/app/controllers/repp/v1/base_controller.rb @@ -0,0 +1,82 @@ +module Repp + module V1 + class BaseController < ActionController::API + rescue_from ActiveRecord::RecordNotFound, with: :not_found_error + before_action :authenticate_user + before_action :check_ip_restriction + + attr_reader :current_user + + rescue_from ActionController::ParameterMissing do |exception| + render json: { code: 2003, message: exception }, status: :bad_request + end + + private + + def epp_errors + @errors ||= [] + end + + def handle_errors(obj = nil, update: false) + @errors ||= [] + + if obj + obj.construct_epp_errors + @errors += obj.errors[:epp_errors] + end + + if update + @errors.each_with_index do |errors, index| + if errors[:code] == '2304' && + errors[:value].present? && + errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + errors[:value][:obj] == 'status' + @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + end + end + end + + @errors.uniq! + + render_epp_error + end + + def render_epp_error + render(json: { code: @errors[0][:code], message: @errors[0][:msg] }, status: :bad_request) + end + + def ip_whitelisted? + return false unless @api_user.registrar.api_ip_white?(request.ip) + end + + def basic_token + pattern = /^Basic / + header = request.headers['Authorization'] + header.gsub(pattern, '') if header&.match(pattern) + end + + def authenticate_user + username, password = Base64.urlsafe_decode64(basic_token).split(':') + @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) + + return if @current_user + + render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + end + + def check_ip_restriction + ip_restriction = Authorization::RestrictedIP.new(request.ip) + allowed = ip_restriction.can_access_registrar_area?(@current_user.registrar) + + return if allowed + + flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) + render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) + end + + def not_found_error + render(json: { code: 2303, message: 'Object does not exist' }, status: :not_found) + end + end + end +end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb new file mode 100644 index 000000000..70bf91297 --- /dev/null +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -0,0 +1,111 @@ +module Repp + module V1 + class ContactsController < BaseController + before_action :find_contact, only: [:update] + + ## GET /repp/v1/contacts + def index + limit = params[:limit] || 200 + offset = params[:offset] || 0 + + record_count = current_user.registrar.contacts.count + contacts = current_user.registrar.contacts.limit(limit).offset(offset) + + unless Contact.address_processing? && params[:details] == 'true' + contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) + end + + contacts = contacts.pluck(:code) unless params[:details] + resp = { contacts: contacts, total_number_of_records: record_count } + render(json: resp, status: :ok) + end + + ## POST /repp/v1/contacts + def create + @legal_doc = params[:legal_documents] + @contact_params = contact_create_params + @ident = contact_ident_params + address_present = contact_addr_params.keys.any? + %w[city street zip country_code].each { |k| @contact_params[k] = contact_addr_params[k] } + + @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + + action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :created) + else + handle_errors(@contact) + end + end + + ## PUT /repp/v1/contacts/1 + def update + @update = contact_create_params + %w[city street zip country_code].each { |k| @new_params[k] = contact_addr_params[k] } + + @legal_doc = params[:legal_document] + @ident = contact_ident_params || {} + address_present = contact_addr_params.keys.any? + action = Actions::ContactUpdate.new(@contact, @update, @legal_doc, @ident, current_user) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :ok) + else + handle_errors(@contact) + end + end + + def find_contact + code = params[:id] + @contact = Epp::Contact.find_by!(code: code) + end + + def contact_create_params + params.require(:contact).require(%i[name email phone]) + params.require(:contact).permit(:name, :email, :phone) + end + + def contact_ident_params + params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) + params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + end + + def contact_addr_params + if Contact.address_processing? + params.require(:contact).require(:addr).require(%i[country_code city street zip]) + params.require(:contact).require(:addr).permit(:country_code, :city, :street, :zip) + else + params.require(:contact).permit(addr: %i[country_code city street zip]) + end + end + + def legal_document_params + params.require(:legal_document).require(%i[body type]) + params.require(:legal_document).permit(:body, :type) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 41f857bc8..afad7b947 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,10 +37,9 @@ Rails.application.routes.draw do get 'error/:command', to: 'errors#error' end - mount Repp::API => '/' - namespace :repp do namespace :v1 do + resources :contacts resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] end From 1686c352bced9bb5bea9499729fa3b72797b59df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 8 Oct 2020 16:28:05 +0300 Subject: [PATCH 010/172] REPP: Port accounts controller from Grape to Rails API --- app/api/repp/account_v1.rb | 16 ---------------- app/controllers/repp/v1/accounts_controller.rb | 11 +++++++++++ config/routes.rb | 5 +++++ 3 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 app/api/repp/account_v1.rb create mode 100644 app/controllers/repp/v1/accounts_controller.rb diff --git a/app/api/repp/account_v1.rb b/app/api/repp/account_v1.rb deleted file mode 100644 index fe3acd387..000000000 --- a/app/api/repp/account_v1.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Repp - class AccountV1 < Grape::API - version 'v1', using: :path - - resource :accounts do - desc 'Return current cash account balance' - - get 'balance' do - @response = { - balance: current_user.registrar.cash_account.balance, - currency: current_user.registrar.cash_account.currency - } - end - end - end -end diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb new file mode 100644 index 000000000..5adf99ea1 --- /dev/null +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -0,0 +1,11 @@ +module Repp + module V1 + class AccountsController < BaseController + def balance + resp = { balance: current_user.registrar.cash_account.balance, + currency: current_user.registrar.cash_account.currency } + render(json: resp, status: :ok) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index afad7b947..5c518a533 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,6 +40,11 @@ Rails.application.routes.draw do namespace :repp do namespace :v1 do resources :contacts + resources :accounts do + collection do + get 'balance' + end + end resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] end From 39c6d204134375fd04765c1b10b4ce11402a2779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 09:34:59 +0300 Subject: [PATCH 011/172] Repp: Add contacts/check action --- .../repp/v1/contacts_controller.rb | 20 ++++++++++++++++++- config/routes.rb | 7 ++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 70bf91297..1d2dbd441 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,7 +1,7 @@ module Repp module V1 class ContactsController < BaseController - before_action :find_contact, only: [:update] + before_action :find_contact, only: %i[show update] ## GET /repp/v1/contacts def index @@ -20,6 +20,24 @@ module Repp render(json: resp, status: :ok) end + ## GET /repp/v1/contacts/1 + def show + render(json: @contact.as_json, status: :ok) + end + + ## GET /repp/v1/contacts/check/1 + def check + contact = Epp::Contact.find_by(code: params[:id]) + + render json: { + code: 1000, message: I18n.t('epp.contacts.completed'), + data: { contact: { + id: params[:id], + available: contact.nil? + } } + }, status: :ok + end + ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] diff --git a/config/routes.rb b/config/routes.rb index 5c518a533..634b40e31 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -39,7 +39,12 @@ Rails.application.routes.draw do namespace :repp do namespace :v1 do - resources :contacts + resources :contacts do + collection do + get 'check/:id', to: 'contacts#check' + end + end + resources :accounts do collection do get 'balance' From a73cd53bff04f28c87030cbf064dfe3f937c77fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 11:12:25 +0300 Subject: [PATCH 012/172] REPP: Verify contact ident type --- app/controllers/repp/v1/base_controller.rb | 3 +-- app/controllers/repp/v1/contacts_controller.rb | 12 ++++++++---- app/models/actions/contact_create.rb | 11 ++++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 30e37b9f7..f1000849f 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -46,7 +46,7 @@ module Repp end def ip_whitelisted? - return false unless @api_user.registrar.api_ip_white?(request.ip) + return false unless current_user.registrar.api_ip_white?(request.ip) end def basic_token @@ -70,7 +70,6 @@ module Repp return if allowed - flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 1d2dbd441..51a03f8d4 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -41,10 +41,9 @@ module Repp ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] - @contact_params = contact_create_params + @contact_params = contact_params_with_address @ident = contact_ident_params address_present = contact_addr_params.keys.any? - %w[city street zip country_code].each { |k| @contact_params[k] = contact_addr_params[k] } @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) @@ -70,8 +69,7 @@ module Repp ## PUT /repp/v1/contacts/1 def update - @update = contact_create_params - %w[city street zip country_code].each { |k| @new_params[k] = contact_addr_params[k] } + @update = contact_params_with_address @legal_doc = params[:legal_document] @ident = contact_ident_params || {} @@ -101,6 +99,12 @@ module Repp @contact = Epp::Contact.find_by!(code: code) end + def contact_params_with_address + addr = {} + contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } + contact_create_params.merge(addr) + end + def contact_create_params params.require(:contact).require(%i[name email phone]) params.require(:contact).permit(:name, :email, :phone) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 4cb6607ae..095c3f61f 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -26,9 +26,14 @@ module Actions end def validate_ident - if ident.present? && ident[:ident_type].blank? - contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) - @error = true + if ident.present? + 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 if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? From 2e8ce7dbc4c23e31afd64e4365089943866d4789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 12:00:57 +0300 Subject: [PATCH 013/172] REPP: Allow contact creation without address --- app/controllers/repp/v1/contacts_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 51a03f8d4..70f5be1b5 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -101,6 +101,8 @@ module Repp def contact_params_with_address addr = {} + return contact_create_params unless contact_addr_params.key?(:addr) + contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } contact_create_params.merge(addr) end From f6580bd79a6e4bf8d1d7579a84f84c8420b70ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 13:10:28 +0300 Subject: [PATCH 014/172] REPP: custom success response handler --- app/controllers/repp/v1/base_controller.rb | 7 ++ .../repp/v1/contacts_controller.rb | 85 +++++++------------ 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index f1000849f..81f812761 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -13,6 +13,13 @@ module Repp private + def render_success(code: nil, message: nil, data: nil) + resp = { code: code || 1000, message: message || 'Command completed successfully', + data: data || {} } + + render(json: resp, status: :ok) + end + def epp_errors @errors ||= [] end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 70f5be1b5..66ab3712f 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -22,76 +22,49 @@ module Repp ## GET /repp/v1/contacts/1 def show - render(json: @contact.as_json, status: :ok) + render_success(data: @contact.as_json) end ## GET /repp/v1/contacts/check/1 def check contact = Epp::Contact.find_by(code: params[:id]) + data = { contact: { id: params[:id], available: contact.nil? } } - render json: { - code: 1000, message: I18n.t('epp.contacts.completed'), - data: { contact: { - id: params[:id], - available: contact.nil? - } } - }, status: :ok + render_success(data: data) end ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] - @contact_params = contact_params_with_address - @ident = contact_ident_params - address_present = contact_addr_params.keys.any? - @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) + action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) + handle_errors(@contact) and return unless action.call - action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) - - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end - - render(json: { code: @response_code, - message: @response_description, - data: { contact: { id: @contact.code } } }, - status: :created) - else - handle_errors(@contact) - end + render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) end ## PUT /repp/v1/contacts/1 def update - @update = contact_params_with_address + action = Actions::ContactUpdate.new(@contact, contact_params_with_address, + params[:legal_document], + contact_ident_params(required: false), current_user) - @legal_doc = params[:legal_document] - @ident = contact_ident_params || {} - address_present = contact_addr_params.keys.any? - action = Actions::ContactUpdate.new(@contact, @update, @legal_doc, @ident, current_user) + handle_errors(@contact) and return unless action.call - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end + render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + end - render(json: { code: @response_code, - message: @response_description, - data: { contact: { id: @contact.code } } }, - status: :ok) - else - handle_errors(@contact) - end + def contact_addr_present? + return false unless contact_addr_params.key?(:addr) + + contact_addr_params[:addr].keys.any? + end + + def opt_addr? + !Contact.address_processing? && contact_addr_present? end def find_contact @@ -100,9 +73,9 @@ module Repp end def contact_params_with_address - addr = {} return contact_create_params unless contact_addr_params.key?(:addr) + addr = {} contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } contact_create_params.merge(addr) end @@ -112,9 +85,13 @@ module Repp params.require(:contact).permit(:name, :email, :phone) end - def contact_ident_params - params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) - params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + def contact_ident_params(required: true) + if required + params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) + params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + else + params.permit(ident: %i[ident ident_type ident_country_code]) + end end def contact_addr_params From eb19412643754f060f6db1c13ded02880cf037b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 12:33:15 +0300 Subject: [PATCH 015/172] Fix some CC issues --- app/controllers/epp/contacts_controller.rb | 3 ++- app/controllers/repp/v1/base_controller.rb | 18 +++++++++++------- app/models/actions/contact_create.rb | 6 ++++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index b674d0919..66791abc5 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -24,7 +24,8 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) + action = Actions::ContactCreate.new(@contact, collected_data.legal_document, + collected_data.ident) if action.call if !address_processing? && address_given? diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 81f812761..45144ca5a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -34,12 +34,11 @@ module Repp if update @errors.each_with_index do |errors, index| - if errors[:code] == '2304' && - errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' - @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE - end + next unless errors[:code] == '2304' && errors[:value].present? && + errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + errors[:value][:obj] == 'status' + + @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE end end @@ -77,7 +76,12 @@ module Repp return if allowed - render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) + render( + status: :unauthorized, + json: { errors: [ + { base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }, + ] } + ) end def not_found_error diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 095c3f61f..ff46ed817 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -28,7 +28,8 @@ module Actions def validate_ident if ident.present? if ident[:ident_type].blank? - contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) + 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') @@ -37,7 +38,8 @@ module Actions end if ident.present? && 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')) + contact.add_epp_error('2003', nil, 'ident_country_code', + I18n.t('errors.messages.required_ident_attribute_missing')) @error = true end From 5cd495b85cdde28cea5a1820ecb0daa824468a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:07:41 +0300 Subject: [PATCH 016/172] REPP: Simplify Contacts#index method --- .../repp/v1/contacts_controller.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 66ab3712f..900c2140d 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -9,13 +9,9 @@ module Repp offset = params[:offset] || 0 record_count = current_user.registrar.contacts.count - contacts = current_user.registrar.contacts.limit(limit).offset(offset) + show_addresses = Contact.address_processing? && params[:details] == 'true' + contacts = showable_contacts(params[:details], limit, offset, show_addresses) - unless Contact.address_processing? && params[:details] == 'true' - contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) - end - - contacts = contacts.pluck(:code) unless params[:details] resp = { contacts: contacts, total_number_of_records: record_count } render(json: resp, status: :ok) end @@ -63,6 +59,17 @@ module Repp contact_addr_params[:addr].keys.any? end + def showable_contacts(details, limit, offset, addresses) + contacts = current_user.registrar.contacts.limit(limit).offset(offset) + unless addresses + contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) + end + + contacts = contacts.pluck(:code) unless details + + contacts + end + def opt_addr? !Contact.address_processing? && contact_addr_present? end From 737939082799ffa4451ee1a5bf1e335f836d9cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:22:42 +0300 Subject: [PATCH 017/172] Fix whining about AndOr --- app/controllers/repp/v1/contacts_controller.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 900c2140d..e7e5c73cf 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -35,7 +35,10 @@ module Repp @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) - handle_errors(@contact) and return unless action.call + unless action.call + handle_errors(@contact) + return + end render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) @@ -47,7 +50,10 @@ module Repp params[:legal_document], contact_ident_params(required: false), current_user) - handle_errors(@contact) and return unless action.call + unless action.call + handle_errors(@contact) + return + end render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) From 8755fbdbf47866d915b9c8e11a9e30aa53d530c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:55:44 +0300 Subject: [PATCH 018/172] REPP: compose success data in new method --- .../repp/v1/contacts_controller.rb | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index e7e5c73cf..b70e67c61 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -5,15 +5,12 @@ module Repp ## GET /repp/v1/contacts def index - limit = params[:limit] || 200 - offset = params[:offset] || 0 - record_count = current_user.registrar.contacts.count show_addresses = Contact.address_processing? && params[:details] == 'true' - contacts = showable_contacts(params[:details], limit, offset, show_addresses) + contacts = showable_contacts(params[:details], params[:limit] || 200, + params[:offset] || 0, show_addresses) - resp = { contacts: contacts, total_number_of_records: record_count } - render(json: resp, status: :ok) + render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) end ## GET /repp/v1/contacts/1 @@ -31,17 +28,16 @@ module Repp ## POST /repp/v1/contacts def create - @legal_doc = params[:legal_documents] - @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) - action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) + action = Actions::ContactCreate.new(@contact, params[:legal_documents], + contact_ident_params) + unless action.call handle_errors(@contact) return end - render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, - message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + render_success(create_update_success_data) end ## PUT /repp/v1/contacts/1 @@ -55,8 +51,7 @@ module Repp return end - render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, - message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + render_success(create_update_success_data) end def contact_addr_present? @@ -65,6 +60,11 @@ module Repp contact_addr_params[:addr].keys.any? end + def create_update_success_body + { code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil } + end + def showable_contacts(details, limit, offset, addresses) contacts = current_user.registrar.contacts.limit(limit).offset(offset) unless addresses From a495c2ab1c37efc4f292abc0eb60bb9b93486aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 15:28:31 +0300 Subject: [PATCH 019/172] Fix CC issues --- app/controllers/repp/v1/base_controller.rb | 17 ++++++++++------- app/controllers/repp/v1/contacts_controller.rb | 7 +++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 45144ca5a..5116a2238 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -21,34 +21,37 @@ module Repp end def epp_errors - @errors ||= [] + @epp_errors ||= [] end def handle_errors(obj = nil, update: false) - @errors ||= [] + @epp_errors ||= [] if obj obj.construct_epp_errors - @errors += obj.errors[:epp_errors] + @epp_errors += obj.errors[:epp_errors] end if update - @errors.each_with_index do |errors, index| + @epp_errors.each_with_index do |errors, index| next unless errors[:code] == '2304' && errors[:value].present? && errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && errors[:value][:obj] == 'status' - @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE end end - @errors.uniq! + @epp_errors.uniq! render_epp_error end def render_epp_error - render(json: { code: @errors[0][:code], message: @errors[0][:msg] }, status: :bad_request) + render( + json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, + status: :bad_request + ) end def ip_whitelisted? diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index b70e67c61..34099bd93 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -6,9 +6,8 @@ module Repp ## GET /repp/v1/contacts def index record_count = current_user.registrar.contacts.count - show_addresses = Contact.address_processing? && params[:details] == 'true' contacts = showable_contacts(params[:details], params[:limit] || 200, - params[:offset] || 0, show_addresses) + params[:offset] || 0) render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) end @@ -65,9 +64,9 @@ module Repp message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil } end - def showable_contacts(details, limit, offset, addresses) + def showable_contacts(details, limit, offset) contacts = current_user.registrar.contacts.limit(limit).offset(offset) - unless addresses + unless Contact.address_processing? && params[:details] == 'true' contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) end From b0f9d316c94a7968c50892669e715fb480fc9339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 16:26:08 +0300 Subject: [PATCH 020/172] REPP: Refactor handle_errors() --- app/controllers/repp/v1/base_controller.rb | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 5116a2238..714fb3a47 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -27,26 +27,25 @@ module Repp def handle_errors(obj = nil, update: false) @epp_errors ||= [] - if obj - obj.construct_epp_errors - @epp_errors += obj.errors[:epp_errors] - end - - if update - @epp_errors.each_with_index do |errors, index| - next unless errors[:code] == '2304' && errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' - - @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE - end - end + obj&.construct_epp_errors + @epp_errors += obj.errors[:epp_errors] if obj + format_epp_errors if update @epp_errors.uniq! render_epp_error end + def format_epp_errors + @epp_errors.each_with_index do |errors, index| + next unless errors[:code] == '2304' && errors[:value].present? && + errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + errors[:value][:obj] == 'status' + + @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + end + end + def render_epp_error render( json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, From 3e27869379f72e4a678f537656c735a1979a57cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 11:00:06 +0300 Subject: [PATCH 021/172] Reuse action call response code --- app/controllers/epp/contacts_controller.rb | 42 +++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 66791abc5..adaa26070 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -27,19 +27,7 @@ module Epp action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) - if action.call - if !address_processing? && address_given? - @response_code = 1100 - @response_description = t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = t('epp.contacts.completed') - end - - render_epp_response '/epp/contacts/save' - else - handle_errors(@contact) - end + action_call_response(action: action) end def update @@ -52,19 +40,7 @@ module Epp collected_data.ident, current_user) - if action.call - if !address_processing? && address_given? - @response_code = 1100 - @response_description = t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = t('epp.contacts.completed') - end - - render_epp_response 'epp/contacts/save' - else - handle_errors(@contact) - end + action_call_response(action: action) end def delete @@ -91,6 +67,20 @@ module Epp private + def action_call_response(action:) + unless action.call + handle_errors(@contact) + return + end + + @response_code = !address_processing? && address_given? ? 1100 : 1000 + str = 'epp.contacts.completed' + str = "#{str}_without_address" if !address_processing? && address_given? + + @response_description = t(str) + render_epp_response('epp/contacts/save') + end + def find_password @password = params[:parsed_frame].css('authInfo pw').text end From c59b47a519e2278c522146cc1aff7f7daa32a509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 11:20:07 +0300 Subject: [PATCH 022/172] REPP: Simplify ident validation --- app/models/actions/contact_create.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index ff46ed817..a771dbde4 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -26,16 +26,7 @@ module Actions end def validate_ident - if ident.present? - 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 + validate_ident_integrity if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? contact.add_epp_error('2003', nil, 'ident_country_code', @@ -50,6 +41,19 @@ module Actions 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 maybe_attach_legal_doc return unless legal_document From cdf28befca91078a4385214ab5da3b61e97eb26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 13:19:57 +0300 Subject: [PATCH 023/172] Fix CC issues --- app/controllers/epp/contacts_controller.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index adaa26070..c28d5256b 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -67,17 +67,21 @@ module Epp private + def opt_addr? + !address_processing? && address_given? + end + def action_call_response(action:) - unless action.call - handle_errors(@contact) - return + (handle_errors(@contact) and return) unless action.call + + if opt_addr? + @response_code = 1100 + @response_description = t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = t('epp.contacts.completed') end - @response_code = !address_processing? && address_given? ? 1100 : 1000 - str = 'epp.contacts.completed' - str = "#{str}_without_address" if !address_processing? && address_given? - - @response_description = t(str) render_epp_response('epp/contacts/save') end From aac74e26f1cabf1ff33952378beaf615a908322a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 13:33:39 +0300 Subject: [PATCH 024/172] Fix some more CC issues --- app/controllers/epp/contacts_controller.rb | 12 ++++-------- app/models/actions/contact_create.rb | 16 ++++++++++------ app/views/epp/contacts/info.xml.builder | 4 ++-- app/views/registrar/contacts/_form.haml | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index c28d5256b..4ef8d11d3 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -4,7 +4,6 @@ module Epp class ContactsController < BaseController before_action :find_contact, only: [:info, :update, :delete] before_action :find_password, only: [:info, :update, :delete] - helper_method :address_processing? def info authorize! :info, @contact, @password @@ -68,11 +67,13 @@ module Epp private def opt_addr? - !address_processing? && address_given? + !Contact.address_processing? && address_given? end def action_call_response(action:) + # rubocop:disable Style/AndOr (handle_errors(@contact) and return) unless action.call + # rubocop:enable Style/AndOr if opt_addr? @response_code = 1100 @@ -123,8 +124,7 @@ module Epp 'postalInfo > addr > cc', ] - required_attributes.concat(address_attributes) if address_processing? - + required_attributes.concat(address_attributes) if Contact.address_processing? requires(*required_attributes) ident = params[:parsed_frame].css('ident') @@ -200,9 +200,5 @@ module Epp def address_given? params[:parsed_frame].css('postalInfo addr').size != 0 end - - def address_processing? - Contact.address_processing? - end end end diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index a771dbde4..13162a553 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -27,12 +27,7 @@ module Actions def validate_ident validate_ident_integrity - - if ident.present? && 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 + validate_ident_birthday identifier = ::Contact::Ident.new(code: ident[:ident], type: ident[:ident_type], country_code: ident[:ident_country_code]) @@ -54,6 +49,15 @@ module Actions 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 diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 1945e7def..776f7ceb2 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -18,7 +18,7 @@ xml.epp_head do if can? :view_full_info, @contact, @password xml.tag!('contact:org', @contact.org_name) if @contact.org_name.present? - if address_processing? + if Contact.address_processing? xml.tag!('contact:addr') do xml.tag!('contact:street', @contact.street) xml.tag!('contact:city', @contact.city) @@ -31,7 +31,7 @@ xml.epp_head do else xml.tag!('contact:org', 'No access') - if address_processing? + if Contact.address_processing? xml.tag!('contact:addr') do xml.tag!('contact:street', 'No access') xml.tag!('contact:city', 'No access') diff --git a/app/views/registrar/contacts/_form.haml b/app/views/registrar/contacts/_form.haml index cf8217e13..953c502e5 100644 --- a/app/views/registrar/contacts/_form.haml +++ b/app/views/registrar/contacts/_form.haml @@ -5,7 +5,7 @@ .col-md-8 = render 'registrar/contacts/form/general', f: f -- if address_processing? +- if Contact.address_processing? .row .col-md-8 = render 'registrar/contacts/form/address', f: f From a782b19d284c70dea7535b91b2b6414b60a80a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 14:43:45 +0300 Subject: [PATCH 025/172] Deserializers: Mirror ContactCreate from ContactUpdate --- app/controllers/repp/v1/base_controller.rb | 12 ++++++++---- .../repp/v1/contacts_controller.rb | 4 ++-- lib/deserializers/xml/contact_create.rb | 19 +------------------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 714fb3a47..3cd636eb2 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -37,10 +37,14 @@ module Repp end def format_epp_errors - @epp_errors.each_with_index do |errors, index| - next unless errors[:code] == '2304' && errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' + @epp_errors.each_with_index do |error, index| + blocked_by_delete_prohibited?(error, index) + end + end + + def blocked_by_delete_prohibited?(error, index) + if error[:code] == 2304 && error[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + error[:value][:obj] == 'status' @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 34099bd93..6321f2a40 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -36,7 +36,7 @@ module Repp return end - render_success(create_update_success_data) + render_success(create_update_success_body) end ## PUT /repp/v1/contacts/1 @@ -50,7 +50,7 @@ module Repp return end - render_success(create_update_success_data) + render_success(create_update_success_body) end def contact_addr_present? diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb index 5bd9c768a..5dfa32ef7 100644 --- a/lib/deserializers/xml/contact_create.rb +++ b/lib/deserializers/xml/contact_create.rb @@ -4,24 +4,7 @@ require 'deserializers/xml/contact' module Deserializers module Xml - class ContactCreate - attr_reader :frame - - def initialize(frame) - @frame = frame - end - - def contact - @contact ||= ::Deserializers::Xml::Contact.new(frame).call - end - - def legal_document - @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call - end - - def ident - @ident ||= ::Deserializers::Xml::Ident.new(frame).call - end + class ContactCreate < ContactUpdate end end end From c31dc15207326a5a47490ef7afea60d7daf7d980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 16:37:47 +0300 Subject: [PATCH 026/172] REPP: Move nameservers endpoint to Rails API format --- app/api/repp/nameservers_v1.rb | 45 ------------------- .../v1/registrar/nameservers_controller.rb | 40 +++++++++++++++++ config/routes.rb | 7 +++ 3 files changed, 47 insertions(+), 45 deletions(-) delete mode 100644 app/api/repp/nameservers_v1.rb create mode 100644 app/controllers/repp/v1/registrar/nameservers_controller.rb diff --git a/app/api/repp/nameservers_v1.rb b/app/api/repp/nameservers_v1.rb deleted file mode 100644 index 04d7d4f6a..000000000 --- a/app/api/repp/nameservers_v1.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Repp - class NameserversV1 < Grape::API - version 'v1', using: :path - - resource 'registrar/nameservers' do - put '/' do - params do - requires :data, type: Hash, allow_blank: false do - requires :type, type: String, allow_blank: false - requires :id, type: String, allow_blank: false - requires :attributes, type: Hash, allow_blank: false do - requires :hostname, type: String, allow_blank: false - requires :ipv4, type: Array - requires :ipv6, type: Array - end - end - end - - hostname = params[:data][:id] - - unless current_user.registrar.nameservers.exists?(hostname: hostname) - error!({ errors: [{ title: "Hostname #{hostname} does not exist" }] }, 404) - end - - new_attributes = { - hostname: params[:data][:attributes][:hostname], - ipv4: params[:data][:attributes][:ipv4], - ipv6: params[:data][:attributes][:ipv6], - } - - begin - affected_domains = current_user.registrar.replace_nameservers(hostname, new_attributes) - rescue ActiveRecord::RecordInvalid => e - error!({ errors: e.record.errors.full_messages.map { |error| { title: error } } }, 400) - end - - status 200 - @response = { data: { type: 'nameserver', - id: params[:data][:attributes][:hostname], - attributes: params[:data][:attributes] }, - affected_domains: affected_domains } - end - end - end -end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb new file mode 100644 index 000000000..877c9bf2e --- /dev/null +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -0,0 +1,40 @@ +module Repp + module V1 + module Registrar + class NameserversController < BaseController + before_action :verify_nameserver_existance, only: %i[update] + + def update + domains = current_user.registrar + .replace_nameservers(hostname, hostname_params[:data][:attributes]) + + render_success(data: data_format_for_success(domains)) + rescue ActiveRecord::RecordInvalid => e + handle_errors(e.record) + end + + private + + def data_format_for_success(affected_domains) + { type: 'nameserver', id: params[:data][:attributes][:hostname], + attributes: params[:data][:attributes], affected_domains: affected_domains } + end + + def hostname_params + params.require(:data).require(%i[type id]) + params.require(:data).require(:attributes).require(%i[hostname ipv4 ipv6]) + + params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) + end + + def hostname + hostname_params[:data][:id] + end + + def verify_nameserver_existance + current_user.registrar.nameservers.find_by!(hostname: hostname) + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 634b40e31..1003a75ce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,13 @@ Rails.application.routes.draw do end resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] + namespace :registrar do + resources :nameservers do + collection do + put '/', to: 'nameservers#update' + end + end + end end end From 6c655e11790f2089504ece347e00096e3fea1bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 10:58:51 +0300 Subject: [PATCH 027/172] REPP: Move Domains#Contacts to rails API format --- app/api/repp/domain_contacts_v1.rb | 47 ------------------- .../repp/v1/domains/contacts_controller.rb | 39 +++++++++++++++ config/routes.rb | 7 +++ 3 files changed, 46 insertions(+), 47 deletions(-) delete mode 100644 app/api/repp/domain_contacts_v1.rb create mode 100644 app/controllers/repp/v1/domains/contacts_controller.rb diff --git a/app/api/repp/domain_contacts_v1.rb b/app/api/repp/domain_contacts_v1.rb deleted file mode 100644 index 7f3e323ac..000000000 --- a/app/api/repp/domain_contacts_v1.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Repp - class DomainContactsV1 < Grape::API - version 'v1', using: :path - - resource :domains do - resource :contacts do - patch '/' do - current_contact = current_user.registrar.contacts - .find_by(code: params[:current_contact_id]) - new_contact = current_user.registrar.contacts.find_by(code: params[:new_contact_id]) - - unless current_contact - error!({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: "No such contact: #{params[:current_contact_id]}"} }, - :bad_request) - end - - unless new_contact - error!({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: "No such contact: #{params[:new_contact_id]}" } }, - :bad_request) - end - - if new_contact.invalid? - error!({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'New contact must be valid' } }, - :bad_request) - end - - if current_contact == new_contact - error!({ error: { type: 'invalid_request_error', - message: 'New contact ID must be different from current' \ - ' contact ID' } }, - :bad_request) - end - - affected_domains, skipped_domains = TechDomainContact - .replace(current_contact, new_contact) - @response = { affected_domains: affected_domains, skipped_domains: skipped_domains } - end - end - end - end -end diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb new file mode 100644 index 000000000..226981b60 --- /dev/null +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -0,0 +1,39 @@ +module Repp + module V1 + module Domains + class ContactsController < BaseController + before_action :set_current_contact, only: [:update] + before_action :set_new_contact, only: [:update] + + def set_current_contact + @current_contact = current_user.registrar.contacts.find_by!( + code: params[:current_contact_id] + ) + end + + def set_new_contact + @new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id]) + end + + def update + @epp_errors << { code: '2304', msg: 'New contact must be valid' } if @new_contact.invalid? + + if @new_contact == @current_contact + @epp_errors << { code: '2304', msg: 'New contact must be different from current' } + end + + affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) + data = { affected_domains: affected, skipped_domains: skipped } + render_success(data: data) + end + + private + + def contact_params + params.require(%i[current_contact_id new_contact_id]) + params.permit(:current_contact_id, :new_contact_id) + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 1003a75ce..f1557a659 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,13 @@ Rails.application.routes.draw do end end end + namespace :domains do + resources :contacts do + collection do + patch '/', to: 'contacts#update' + end + end + end end end From d1ab61f424daa2f0da4595c1b8a1fc6376ca89cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 12:42:15 +0300 Subject: [PATCH 028/172] REPP: Move domain transfer info to rails API format --- app/api/repp/domain_v1.rb | 50 ---------------- app/controllers/repp/v1/base_controller.rb | 4 +- app/controllers/repp/v1/domains_controller.rb | 60 +++++++++++++++++++ config/routes.rb | 6 ++ 4 files changed, 68 insertions(+), 52 deletions(-) delete mode 100644 app/api/repp/domain_v1.rb create mode 100644 app/controllers/repp/v1/domains_controller.rb diff --git a/app/api/repp/domain_v1.rb b/app/api/repp/domain_v1.rb deleted file mode 100644 index cf45bfc6f..000000000 --- a/app/api/repp/domain_v1.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Repp - class DomainV1 < Grape::API - version 'v1', using: :path - - resource :domains do - desc 'Return list of domains' - params do - optional :limit, type: Integer, values: (1..200).to_a, desc: 'How many domains to show' - optional :offset, type: Integer, desc: 'Domain number to start at' - optional :details, type: String, values: %w(true false), desc: 'Whether to include details' - end - - get '/' do - limit = params[:limit] || 200 - offset = params[:offset] || 0 - - if params[:details] == 'true' - domains = current_user.registrar.domains.limit(limit).offset(offset) - else - domains = current_user.registrar.domains.limit(limit).offset(offset).pluck(:name) - end - - @response = { - domains: domains, - total_number_of_records: current_user.registrar.domains.count - } - end - - # example: curl -u registrar1:password localhost:3000/repp/v1/domains/1/transfer_info -H "Auth-Code: authinfopw1" - get '/:id/transfer_info', requirements: { id: /.*/ } do - ident = params[:id] - domain = ident.match?(/\A[0-9]+\z/) ? Domain.find_by(id: ident) : Domain.find_by_idn(ident) - - error! I18n.t('errors.messages.epp_domain_not_found'), 404 unless domain - error! I18n.t('errors.messages.epp_authorization_error'), 401 unless domain.transfer_code.eql? request.headers['Auth-Code'] - - contact_repp_json = proc{|contact| - contact.as_json.slice("code", "name", "ident", "ident_type", "ident_country_code", "phone", "email", "street", "city", "zip","country_code", "statuses") - } - - @response = { - domain: domain.name, - registrant: contact_repp_json.call(domain.registrant), - admin_contacts: domain.admin_contacts.map{|e| contact_repp_json.call(e)}, - tech_contacts: domain.tech_contacts.map{|e| contact_repp_json.call(e)} - } - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 3cd636eb2..882c26fb0 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -50,10 +50,10 @@ module Repp end end - def render_epp_error + def render_epp_error(status = :bad_request) render( json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, - status: :bad_request + status: status ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb new file mode 100644 index 000000000..e2188730d --- /dev/null +++ b/app/controllers/repp/v1/domains_controller.rb @@ -0,0 +1,60 @@ +module Repp + module V1 + class DomainsController < BaseController + before_action :set_authorized_domain, only: [:transfer_info] + + def index + records = current_user.registrar.domains + domains = records.limit(limit).offset(offset) + domains = domains.pluck(:name) unless params[:details] == 'true' + + render_success(data: { domains: domains, total_number_of_records: records.count }) + end + + def transfer_info + contact_fields = %i[code name ident ident_type ident_country_code phone email street city + zip country_code statuses] + + data = { + domain: @domain.name, + registrant: @domain.registrant.as_json(only: contact_fields), + admin_contacts: @domain.admin_contacts.map { |c| c.as_json(only: contact_fields) }, + tech_contacts: @domain.tech_contacts.map { |c| c.as_json(only: contact_fields) }, + } + + render_success(data: data) + end + + private + + def transfer_info_params + params.require(:id) + params.permit(:id) + end + + def set_authorized_domain + @epp_errors ||= [] + h = {} + h[transfer_info_params[:id].match?(/\A[0-9]+\z/) ? :id : :name] = transfer_info_params[:id] + @domain = Domain.find_by!(h) + + return if @domain.transfer_code.eql?(request.headers['Auth-Code']) + + @epp_errors << { code: '401', msg: I18n.t('errors.messages.epp_authorization_error') } + handle_errors + end + + def limit + params[:limit] || 200 + end + + def offset + params[:offset] || 0 + end + + def index_params + params.permit(:limit, :offset, :details) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index f1557a659..39cc4a0c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,12 @@ Rails.application.routes.draw do end end end + resources :domains do + collection do + get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } + end + end + namespace :domains do resources :contacts do collection do From ee199f9318cf3b22629c24fe127289ee44257937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 15:46:57 +0300 Subject: [PATCH 029/172] REPP: Move domain transfer to rails API format --- app/api/repp/domain_transfers_v1.rb | 48 ------------------- app/controllers/repp/v1/domains_controller.rb | 41 ++++++++++++++++ config/routes.rb | 1 + 3 files changed, 42 insertions(+), 48 deletions(-) delete mode 100644 app/api/repp/domain_transfers_v1.rb diff --git a/app/api/repp/domain_transfers_v1.rb b/app/api/repp/domain_transfers_v1.rb deleted file mode 100644 index c6a48df6d..000000000 --- a/app/api/repp/domain_transfers_v1.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Repp - class DomainTransfersV1 < Grape::API - version 'v1', using: :path - - resource :domain_transfers do - post '/' do - params do - requires :data, type: Hash do - requires :domainTransfers, type: Array do - requires :domainName, type: String, allow_blank: false - requires :transferCode, type: String, allow_blank: false - end - end - end - - new_registrar = current_user.registrar - domain_transfers = params['data']['domainTransfers'] - successful_domain_transfers = [] - errors = [] - - domain_transfers.each do |domain_transfer| - domain_name = domain_transfer['domainName'] - transfer_code = domain_transfer['transferCode'] - domain = Domain.find_by(name: domain_name) - - if domain - if domain.transfer_code == transfer_code - DomainTransfer.request(domain, new_registrar) - successful_domain_transfers << { type: 'domain_transfer', attributes: { domain_name: domain.name } } - else - errors << { title: "#{domain_name} transfer code is wrong" } - end - else - errors << { title: "#{domain_name} does not exist" } - end - end - - if errors.none? - status 200 - @response = { data: successful_domain_transfers } - else - status 400 - @response = { errors: errors } - end - end - end - end -end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index e2188730d..4f3d8afe3 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -25,8 +25,49 @@ module Repp render_success(data: data) end + def transfer + @errors ||= [] + successful = [] + + params[:data][:domain_transfers].each do |transfer| + domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) + next unless domain + + DomainTransfer.request(domain, current_user.registrar) + successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } + end + + render_success(data: { errors: @errors }) and return if @errors.any? + + render_success(data: successful) + end + + def transferable_domain(domain_name, transfer_code) + domain = Domain.find_by(name: domain_name) + valid_transfer_code = domain.transfer_code == transfer_code + add_error("#{domain_name} does not exist") and return unless domain + add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code + + domain + end + + def add_error(msg) + @errors ||= [] + @errors << { title: msg } + end + private + def transfer_params + params.require(:data).require(:domain_transfers).each do |t| + t.require(:domain_name) + t.permit(:domain_name) + t.require(:transfer_code) + t.permit(:transfer_code) + end + params.require(:data).permit(domain_transfers: %i[domain_name transfer_code]) + end + def transfer_info_params params.require(:id) params.permit(:id) diff --git a/config/routes.rb b/config/routes.rb index 39cc4a0c3..3827d8ef9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -62,6 +62,7 @@ Rails.application.routes.draw do resources :domains do collection do get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } + post 'transfer', to: 'domains#transfer' end end From 9b8d74fe30e7e46a8710340d207ca4455324020d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 15:53:54 +0300 Subject: [PATCH 030/172] Fic some CC issues --- app/controllers/repp/v1/domains_controller.rb | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 4f3d8afe3..32ae61687 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -27,26 +27,34 @@ module Repp def transfer @errors ||= [] - successful = [] + @successful = [] params[:data][:domain_transfers].each do |transfer| - domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - next unless domain - - DomainTransfer.request(domain, current_user.registrar) - successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } + initiate_transfer(transfer) end - render_success(data: { errors: @errors }) and return if @errors.any? + if @errors.any + render_success(data: { errors: @errors }) + else + render_success(data: successful) + end + end - render_success(data: successful) + def initiate_transfer(transfer) + domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) + next unless domain + + DomainTransfer.request(domain, current_user.registrar) + @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } end def transferable_domain(domain_name, transfer_code) domain = Domain.find_by(name: domain_name) valid_transfer_code = domain.transfer_code == transfer_code + # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code + # rubocop:enable Style/AndOr domain end From 6d96a09b5cfdfb3df982ff40f153fe6a686a5a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:04:27 +0300 Subject: [PATCH 031/172] Save REPP log after every request --- app/controllers/repp/v1/base_controller.rb | 11 ++++++++++- app/controllers/repp/v1/domains_controller.rb | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 882c26fb0..7aac47d80 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,13 +4,22 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction - attr_reader :current_user rescue_from ActionController::ParameterMissing do |exception| render json: { code: 2003, message: exception }, status: :bad_request end + after_action do + ApiLog::ReppLog.create( + { request_path: request.path, request_method: request.request_method, + request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), + response: @response.to_json, response_code: status, ip: request.ip, + api_user_name: current_user.try(:username), + api_user_registrar: current_user.try(:registrar).try(:to_s) } + ) + end + private def render_success(code: nil, message: nil, data: nil) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 32ae61687..e130d7a68 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,7 +33,7 @@ module Repp initiate_transfer(transfer) end - if @errors.any + if @errors.any? render_success(data: { errors: @errors }) else render_success(data: successful) @@ -42,7 +42,7 @@ module Repp def initiate_transfer(transfer) domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - next unless domain + return unless domain DomainTransfer.request(domain, current_user.registrar) @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } From 951a3f247b8f3251c2c0e4de7226543651985a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:43:45 +0300 Subject: [PATCH 032/172] Fix some CC issues --- app/controllers/repp/v1/base_controller.rb | 10 +++++----- app/controllers/repp/v1/domains_controller.rb | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 7aac47d80..38f3a486a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -12,11 +12,11 @@ module Repp after_action do ApiLog::ReppLog.create( - { request_path: request.path, request_method: request.request_method, - request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), - response: @response.to_json, response_code: status, ip: request.ip, - api_user_name: current_user.try(:username), - api_user_registrar: current_user.try(:registrar).try(:to_s) } + request_path: request.path, request_method: request.request_method, + request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), + response: @response.to_json, response_code: status, ip: request.ip, + api_user_name: current_user.try(:username), + api_user_registrar: current_user.try(:registrar).try(:to_s) ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index e130d7a68..0d1470e6a 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -50,9 +50,9 @@ module Repp def transferable_domain(domain_name, transfer_code) domain = Domain.find_by(name: domain_name) - valid_transfer_code = domain.transfer_code == transfer_code # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain + valid_transfer_code = domain.transfer_code.eql?(transfer_code) add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code # rubocop:enable Style/AndOr From b8311ac5f6b253fd0691e155d88e330619e5d45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:48:46 +0300 Subject: [PATCH 033/172] Remove Grape from codebase --- Gemfile | 2 -- Gemfile.lock | 34 --------------------- app/api/repp/api.rb | 73 --------------------------------------------- 3 files changed, 109 deletions(-) delete mode 100644 app/api/repp/api.rb diff --git a/Gemfile b/Gemfile index 6ba54e871..34bc9b7ac 100644 --- a/Gemfile +++ b/Gemfile @@ -35,8 +35,6 @@ gem 'select2-rails', '3.5.9.3' # for autocomplete gem 'cancancan' gem 'devise', '~> 4.7' -gem 'grape' - # registry specfic gem 'data_migrate', '~> 6.1' gem 'isikukood' # for EE-id validation diff --git a/Gemfile.lock b/Gemfile.lock index 1a45ed826..ff6dcd3a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -196,28 +196,6 @@ GEM docile (1.3.2) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dry-configurable (0.11.6) - concurrent-ruby (~> 1.0) - dry-core (~> 0.4, >= 0.4.7) - dry-equalizer (~> 0.2) - dry-container (0.7.2) - concurrent-ruby (~> 1.0) - dry-configurable (~> 0.1, >= 0.1.3) - dry-core (0.4.9) - concurrent-ruby (~> 1.0) - dry-equalizer (0.3.0) - dry-inflector (0.2.0) - dry-logic (1.0.7) - concurrent-ruby (~> 1.0) - dry-core (~> 0.2) - dry-equalizer (~> 0.2) - dry-types (1.4.0) - concurrent-ruby (~> 1.0) - dry-container (~> 0.3) - dry-core (~> 0.4, >= 0.4.4) - dry-equalizer (~> 0.3) - dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.0, >= 1.0.2) erubi (1.9.0) erubis (2.7.0) execjs (2.7.0) @@ -226,13 +204,6 @@ GEM thor (~> 0.14) globalid (0.4.2) activesupport (>= 4.2.0) - grape (1.4.0) - activesupport - builder - dry-types (>= 1.1) - mustermann-grape (~> 1.0.0) - rack (>= 1.3.0) - rack-accept gyoku (1.3.1) builder (>= 2.1.2) haml (5.1.2) @@ -312,8 +283,6 @@ GEM multi_json (1.15.0) mustermann (1.1.1) ruby2_keywords (~> 0.0.1) - mustermann-grape (1.0.1) - mustermann (>= 1.0.0) netrc (0.11.0) nio4r (2.5.4) nokogiri (1.10.10) @@ -357,8 +326,6 @@ GEM que (~> 0.8) sinatra rack (2.2.3) - rack-accept (0.4.5) - rack (>= 0.4) rack-oauth2 (1.16.0) activesupport attr_required @@ -542,7 +509,6 @@ DEPENDENCIES epp! epp-xml (= 1.1.0)! figaro (= 1.1.1) - grape haml (~> 5.0) isikukood iso8601 (= 0.12.1) diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb deleted file mode 100644 index f1908f81e..000000000 --- a/app/api/repp/api.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Repp - class API < Grape::API - format :json - prefix :repp - - rescue_from Grape::Exceptions::ValidationErrors do |e| - messages = e.full_messages - errors = [] - messages.each { |m| errors << { code: 2003, message: m } } - - error!({ errors: errors }, 400) - end - - http_basic do |username, password| - @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) - if @current_user - true - else - error! I18n.t('api_user_not_found'), 401 - end - end - - before do - webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip) - unless webclient_request - error! I18n.t('api.authorization.ip_not_allowed', ip: request.ip), 401 unless @current_user.registrar.api_ip_white?(request.ip) - end - - if @current_user.cannot?(:view, :repp) - error! I18n.t('no_permission'), 401 unless @current_user.registrar.api_ip_white?(request.ip) - end - - next if Rails.env.test? || Rails.env.development? - message = 'Certificate mismatch! Cert common name should be:' - request_name = env['HTTP_SSL_CLIENT_S_DN_CN'] - - if webclient_request - webclient_cert_name = ENV['webclient_cert_common_name'] || 'webclient' - error! "Webclient #{message} #{webclient_cert_name}", 401 if webclient_cert_name != request_name - else - unless @current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], - request.env['HTTP_SSL_CLIENT_S_DN_CN']) - error! "#{message} #{@current_user.username}", 401 - end - end - end - - helpers do - attr_reader :current_user - end - - after do - ApiLog::ReppLog.create({ - request_path: request.path, - request_method: request.request_method, - request_params: request.params.except('route_info').to_json, - response: @response.to_json, - response_code: status, - api_user_name: current_user.try(:username), - api_user_registrar: current_user.try(:registrar).try(:to_s), - ip: request.ip, - uuid: request.try(:uuid) - }) - end - - mount Repp::DomainV1 - mount Repp::ContactV1 - mount Repp::AccountV1 - mount Repp::DomainTransfersV1 - mount Repp::NameserversV1 - mount Repp::DomainContactsV1 - end -end From 27774cc275089feec5ea8c62a39a2834015e4235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 17:09:47 +0300 Subject: [PATCH 034/172] REPP: Fix tests --- test/integration/api/domain_transfers_test.rb | 20 +++++++++---------- test/integration/api/nameservers/put_test.rb | 7 +++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index aabaeb728..25d245265 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -13,7 +13,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest end def test_returns_domain_transfers - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 assert_equal ({ data: [{ @@ -27,19 +27,19 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_creates_new_domain_transfer assert_difference -> { @domain.transfers.size } do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_approves_automatically_if_auto_approval_is_enabled - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert @domain.transfers.last.approved? end def test_assigns_new_registrar - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } @domain.reload assert_equal @new_registrar, @domain.registrar @@ -48,7 +48,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_regenerates_transfer_code @old_transfer_code = @domain.transfer_code - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } @domain.reload refute_equal @domain.transfer_code, @old_transfer_code @@ -58,26 +58,26 @@ class APIDomainTransfersTest < ApplicationIntegrationTest @old_registrar = @domain.registrar assert_difference -> { @old_registrar.notifications.count } do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_duplicates_registrant_admin_and_tech_contacts assert_difference -> { @new_registrar.contacts.size }, 3 do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_reuses_identical_contact - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_equal 1, @new_registrar.contacts.where(name: 'William').size end def test_fails_if_domain_does_not_exist - post '/repp/v1/domain_transfers', + post '/repp/v1/domains/transfer', params: { data: { domainTransfers: [{ domainName: 'non-existent.test', transferCode: 'any' }] } }, as: :json, @@ -88,7 +88,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest end def test_fails_if_transfer_code_is_wrong - post '/repp/v1/domain_transfers', + post '/repp/v1/domains/transfer', params: { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: 'wrong' }] } }, as: :json, diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index 853a20549..d564c5cda 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -60,7 +60,9 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ data: { type: 'nameserver', + assert_equal ({ code: '1000', + message: 'Command completed successfully', + data: { type: 'nameserver', id: 'ns55.bestnames.test', attributes: { hostname: 'ns55.bestnames.test', ipv4: ['192.0.2.55'], @@ -96,7 +98,8 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ errors: [{ title: 'Hostname is missing' }] }), + assert_equal ({ code: '2003', + message: 'param is missing or the value is empty: hostname' }), JSON.parse(response.body, symbolize_names: true) end From a6f7af0f03fa655a62462da13bbb77381c215374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 15 Oct 2020 10:23:50 +0300 Subject: [PATCH 035/172] Fix tests --- .../registrar/domain_transfers_controller.rb | 6 ++-- app/controllers/repp/v1/base_controller.rb | 2 ++ app/controllers/repp/v1/domains_controller.rb | 2 +- .../v1/registrar/nameservers_controller.rb | 2 +- config/routes.rb | 2 +- test/integration/api/domain_transfers_test.rb | 31 ++++++++++++------- test/integration/api/nameservers/put_test.rb | 10 +++--- .../bulk_change/bulk_transfer_test.rb | 6 ++-- 8 files changed, 35 insertions(+), 26 deletions(-) diff --git a/app/controllers/registrar/domain_transfers_controller.rb b/app/controllers/registrar/domain_transfers_controller.rb index acacc3ef4..ca08c73cf 100644 --- a/app/controllers/registrar/domain_transfers_controller.rb +++ b/app/controllers/registrar/domain_transfers_controller.rb @@ -15,12 +15,12 @@ class Registrar csv.each do |row| domain_name = row['Domain'] transfer_code = row['Transfer code'] - domain_transfers << { 'domainName' => domain_name, 'transferCode' => transfer_code } + domain_transfers << { 'domain_name' => domain_name, 'transfer_code' => transfer_code } end - uri = URI.parse("#{ENV['repp_url']}domain_transfers") + uri = URI.parse("#{ENV['repp_url']}domains/transfer") request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') - request.body = { data: { domainTransfers: domain_transfers } }.to_json + request.body = { data: { domain_transfers: domain_transfers } }.to_json request.basic_auth(current_registrar_user.username, current_registrar_user.plain_text_password) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 38f3a486a..f8a465e77 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -82,6 +82,8 @@ module Repp return if @current_user + render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + rescue NoMethodError render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 0d1470e6a..237ed5fe4 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -36,7 +36,7 @@ module Repp if @errors.any? render_success(data: { errors: @errors }) else - render_success(data: successful) + render_success(data: @successful) end end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 877c9bf2e..7e00cf2ac 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -22,7 +22,7 @@ module Repp def hostname_params params.require(:data).require(%i[type id]) - params.require(:data).require(:attributes).require(%i[hostname ipv4 ipv6]) + params.require(:data).require(:attributes) params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) end diff --git a/config/routes.rb b/config/routes.rb index 3827d8ef9..282a44933 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ Rails.application.routes.draw do namespace :domains do resources :contacts do collection do - patch '/', to: 'contacts#update' + patch '/', to: 'domains/contacts#update' end end end diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index 25d245265..ce6235268 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -16,13 +16,20 @@ class APIDomainTransfersTest < ApplicationIntegrationTest post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ data: [{ - type: 'domain_transfer', - attributes: { - domain_name: 'shop.test' - }, - }] }), - JSON.parse(response.body, symbolize_names: true) + + expected_body = { + code: 1000, + message: 'Command completed successfully', + data: [ + { + type: 'domain_transfer', + attributes: { domain_name: 'shop.test' }, + } + ] + } + + real_body = JSON.parse(response.body, symbolize_names: true) + assert_equal(expected_body, real_body) end def test_creates_new_domain_transfer @@ -78,8 +85,8 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_fails_if_domain_does_not_exist post '/repp/v1/domains/transfer', - params: { data: { domainTransfers: [{ domainName: 'non-existent.test', - transferCode: 'any' }] } }, + params: { data: { domain_transfers: [{ domain_name: 'non-existent.test', + transfer_code: 'any' }] } }, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 @@ -89,8 +96,8 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_fails_if_transfer_code_is_wrong post '/repp/v1/domains/transfer', - params: { data: { domainTransfers: [{ domainName: 'shop.test', - transferCode: 'wrong' }] } }, + params: { data: { domain_transfers: [{ domain_name: 'shop.test', + transfer_code: 'wrong' }] } }, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 @@ -102,7 +109,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest private def request_params - { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: '65078d5' }] } } + { data: { domain_transfers: [{ domain_name: 'shop.test', transfer_code: '65078d5' }] } } end def http_auth_key diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index d564c5cda..3ab4f4dd4 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -60,14 +60,14 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ code: '1000', + assert_equal ({ code: 1000, message: 'Command completed successfully', data: { type: 'nameserver', id: 'ns55.bestnames.test', attributes: { hostname: 'ns55.bestnames.test', ipv4: ['192.0.2.55'], - ipv6: ['2001:db8::55'] } }, - affected_domains: ["airport.test", "shop.test"] }), + ipv6: ['2001:db8::55'] }, + affected_domains: ["airport.test", "shop.test"] }}), JSON.parse(response.body, symbolize_names: true) end @@ -87,7 +87,7 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 404 - assert_equal ({ errors: [{ title: 'Hostname non-existent.test does not exist' }] }), + assert_equal ({code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -98,7 +98,7 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ code: '2003', + assert_equal ({ code: 2003, message: 'param is missing or the value is empty: hostname' }), JSON.parse(response.body, symbolize_names: true) end diff --git a/test/system/registrar_area/bulk_change/bulk_transfer_test.rb b/test/system/registrar_area/bulk_change/bulk_transfer_test.rb index 69b755499..a531ff4dc 100644 --- a/test/system/registrar_area/bulk_change/bulk_transfer_test.rb +++ b/test/system/registrar_area/bulk_change/bulk_transfer_test.rb @@ -6,9 +6,9 @@ class RegistrarAreaBulkTransferTest < ApplicationSystemTestCase end def test_transfer_multiple_domains_in_bulk - request_body = { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: '65078d5' }] } } + request_body = { data: { domain_transfers: [{ domain_name: 'shop.test', transfer_code: '65078d5' }] } } headers = { 'Content-type' => Mime[:json] } - request_stub = stub_request(:post, /domain_transfers/).with(body: request_body, + request_stub = stub_request(:post, /domains\/transfer/).with(body: request_body, headers: headers, basic_auth: ['test_goodnames', 'testtest']) .to_return(body: { data: [{ @@ -29,7 +29,7 @@ class RegistrarAreaBulkTransferTest < ApplicationSystemTestCase def test_fail_gracefully body = { errors: [{ title: 'epic fail' }] }.to_json headers = { 'Content-type' => Mime[:json] } - stub_request(:post, /domain_transfers/).to_return(status: 400, body: body, headers: headers) + stub_request(:post, /domains\/transfer/).to_return(status: 400, body: body, headers: headers) visit registrar_domains_url click_link 'Bulk change' From f2a1ee101b5055e515ff7dbd5fe32b5f0a72dc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 16 Oct 2020 12:12:17 +0300 Subject: [PATCH 036/172] Fix tests --- config/routes.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 4eb09544f..47c4d0943 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,14 +63,7 @@ Rails.application.routes.draw do collection do get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } post 'transfer', to: 'domains#transfer' - end - end - - namespace :domains do - resources :contacts do - collection do - patch '/', to: 'domains/contacts#update' - end + patch 'contacts', to: 'domains/contacts#update' end end end From 220e0d79933b244f5f4fe2e017a581df89d5765d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 16 Oct 2020 12:33:50 +0300 Subject: [PATCH 037/172] Fix tests --- app/controllers/repp/v1/base_controller.rb | 7 +++-- .../repp/v1/domains/contacts_controller.rb | 7 +++-- app/controllers/repp/v1/domains_controller.rb | 9 +++--- .../v1/registrar/nameservers_controller.rb | 2 +- test/integration/api/domain_contacts_test.rb | 31 +++++++------------ test/integration/api/domain_transfers_test.rb | 4 +-- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index f8a465e77..b3aa786d1 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -59,9 +59,12 @@ module Repp end end - def render_epp_error(status = :bad_request) + def render_epp_error(status = :bad_request, data = {}) + @epp_errors ||= [] + @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} + render( - json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, + json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg], data: data }, status: status ) end diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 226981b60..70f64ed28 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -16,12 +16,15 @@ module Repp end def update - @epp_errors << { code: '2304', msg: 'New contact must be valid' } if @new_contact.invalid? + @epp_errors ||= [] + @epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid? if @new_contact == @current_contact - @epp_errors << { code: '2304', msg: 'New contact must be different from current' } + @epp_errors << { code: 2304, msg: 'New contact must be different from current' } end + return handle_errors if @epp_errors.any? + affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) data = { affected_domains: affected, skipped_domains: skipped } render_success(data: data) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 237ed5fe4..163731c69 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -34,7 +34,7 @@ module Repp end if @errors.any? - render_success(data: { errors: @errors }) + render_epp_error(:bad_request, @errors) else render_success(data: @successful) end @@ -52,10 +52,11 @@ module Repp domain = Domain.find_by(name: domain_name) # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain - valid_transfer_code = domain.transfer_code.eql?(transfer_code) - add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code # rubocop:enable Style/AndOr - + unless domain.transfer_code.eql?(transfer_code) + add_error("#{domain_name} transfer code is wrong") + return + end domain end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 7e00cf2ac..1af85b760 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -22,7 +22,7 @@ module Repp def hostname_params params.require(:data).require(%i[type id]) - params.require(:data).require(:attributes) + params.require(:data).require(:attributes).require([:hostname]) params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) end diff --git a/test/integration/api/domain_contacts_test.rb b/test/integration/api/domain_contacts_test.rb index 5336cc10a..6704739d1 100644 --- a/test/integration/api/domain_contacts_test.rb +++ b/test/integration/api/domain_contacts_test.rb @@ -27,8 +27,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :ok - assert_equal ({ affected_domains: %w[airport.test shop.test], - skipped_domains: [] }), + assert_equal ({ code: 1000, message: 'Command completed successfully', data: { affected_domains: %w[airport.test shop.test], + skipped_domains: [] }}), JSON.parse(response.body, symbolize_names: true) end @@ -42,7 +42,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest assert_response :ok assert_equal %w[airport.test shop.test], JSON.parse(response.body, - symbolize_names: true)[:skipped_domains] + symbolize_names: true)[:data][:skipped_domains] end def test_keep_other_tech_contacts_intact @@ -66,10 +66,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'william-002' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: 'No such contact: jack-001' } }), + assert_response :not_found + assert_equal ({ code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -77,10 +75,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest patch '/repp/v1/domains/contacts', params: { current_contact_id: 'non-existent', new_contact_id: 'john-001' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: 'No such contact: non-existent' } }), + assert_response :not_found + assert_equal ({ code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -88,10 +84,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest patch '/repp/v1/domains/contacts', params: { current_contact_id: 'william-001', new_contact_id: 'non-existent' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'No such contact: non-existent' } }), + assert_response :not_found + assert_equal ({code: 2303, message: 'Object does not exist'}), JSON.parse(response.body, symbolize_names: true) end @@ -100,9 +94,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'invalid' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'New contact must be valid' } }), + assert_equal ({ code: 2304, message: 'New contact must be valid', data: {} }), JSON.parse(response.body, symbolize_names: true) end @@ -111,8 +103,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'william-001' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - message: 'New contact ID must be different from current contact ID' } }), + assert_equal ({ code: 2304, message: 'New contact must be different from current', data: {} }), JSON.parse(response.body, symbolize_names: true) end diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index ce6235268..ecb69d262 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -90,7 +90,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ errors: [{ title: 'non-existent.test does not exist' }] }), + assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'non-existent.test does not exist' }] }), JSON.parse(response.body, symbolize_names: true) end @@ -102,7 +102,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 refute_equal @new_registrar, @domain.registrar - assert_equal ({ errors: [{ title: 'shop.test transfer code is wrong' }] }), + assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'shop.test transfer code is wrong' }] }), JSON.parse(response.body, symbolize_names: true) end From 87dff41a4e1177a271517421a3a491f3255f86f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 16:02:18 +0300 Subject: [PATCH 038/172] REPP: Authentication test --- app/controllers/repp/v1/base_controller.rb | 12 ++++++--- .../repp/{ => v1}/auctions_test.rb | 0 test/integration/repp/v1/base_test.rb | 26 +++++++++++++++++++ test/integration/repp/v1/contacts_test.rb | 24 +++++++++++++++++ .../repp/{ => v1}/retained_domains_test.rb | 0 5 files changed, 58 insertions(+), 4 deletions(-) rename test/integration/repp/{ => v1}/auctions_test.rb (100%) create mode 100644 test/integration/repp/v1/base_test.rb create mode 100644 test/integration/repp/v1/contacts_test.rb rename test/integration/repp/{ => v1}/retained_domains_test.rb (100%) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index b3aa786d1..c46c837f6 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -76,7 +76,8 @@ module Repp def basic_token pattern = /^Basic / header = request.headers['Authorization'] - header.gsub(pattern, '') if header&.match(pattern) + header = header.gsub(pattern, '') if header&.match(pattern) + header.strip end def authenticate_user @@ -85,9 +86,12 @@ module Repp return if @current_user - render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) - rescue NoMethodError - render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + raise(ArgumentError) + rescue NoMethodError, ArgumentError + render( + json: { code: 2202, message: 'Invalid authorization information' }, + status: :unauthorized + ) end def check_ip_restriction diff --git a/test/integration/repp/auctions_test.rb b/test/integration/repp/v1/auctions_test.rb similarity index 100% rename from test/integration/repp/auctions_test.rb rename to test/integration/repp/v1/auctions_test.rb diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb new file mode 100644 index 000000000..931ad094c --- /dev/null +++ b/test/integration/repp/v1/base_test.rb @@ -0,0 +1,26 @@ +require 'test_helper' + +class ReppV1BaseTest < ActionDispatch::IntegrationTest + def setup + @registrant = users(:api_bestnames) + token = Base64.encode64("#{@registrant.username}:#{@registrant.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_unauthorized_user_has_no_access + get repp_v1_contacts_path + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] + end + + def test_authenticates_valid_user + get repp_v1_contacts_path, headers: @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + end +end diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts_test.rb new file mode 100644 index 000000000..92c254dc3 --- /dev/null +++ b/test/integration/repp/v1/contacts_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class ReppV1ContactsTest < ActionDispatch::IntegrationTest + def setup + @auction = auctions(:one) + @auction.update!(uuid: '1b3ee442-e8fe-4922-9492-8fcb9dccc69c', + domain: 'auction.test', + status: Auction.statuses[:started]) + end + + def test_get_index + get repp_v1_contacts_path + response_json = JSON.parse(response.body, symbolize_names: true) + + puts response_json + + assert response_json[:count] == 1 + + expected_response = [{ domain_name: @auction.domain, + punycode_domain_name: @auction.domain }] + + assert_equal expected_response, response_json[:auctions] + end +end diff --git a/test/integration/repp/retained_domains_test.rb b/test/integration/repp/v1/retained_domains_test.rb similarity index 100% rename from test/integration/repp/retained_domains_test.rb rename to test/integration/repp/v1/retained_domains_test.rb From d1e877502de667fbf66e7305dad37e8455d79a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 16:29:28 +0300 Subject: [PATCH 039/172] REPP: Contact list tests --- test/integration/repp/v1/base_test.rb | 4 +- test/integration/repp/v1/contacts_test.rb | 55 ++++++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb index 931ad094c..63255dd24 100644 --- a/test/integration/repp/v1/base_test.rb +++ b/test/integration/repp/v1/base_test.rb @@ -2,8 +2,8 @@ require 'test_helper' class ReppV1BaseTest < ActionDispatch::IntegrationTest def setup - @registrant = users(:api_bestnames) - token = Base64.encode64("#{@registrant.username}:#{@registrant.plain_text_password}") + @registrar = users(:api_bestnames) + token = Base64.encode64("#{@registrar.username}:#{@registrar.plain_text_password}") token = "Basic #{token}" @auth_headers = { 'Authorization' => token } diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts_test.rb index 92c254dc3..353cb3758 100644 --- a/test/integration/repp/v1/contacts_test.rb +++ b/test/integration/repp/v1/contacts_test.rb @@ -2,23 +2,54 @@ require 'test_helper' class ReppV1ContactsTest < ActionDispatch::IntegrationTest def setup - @auction = auctions(:one) - @auction.update!(uuid: '1b3ee442-e8fe-4922-9492-8fcb9dccc69c', - domain: 'auction.test', - status: Auction.statuses[:started]) + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } end - def test_get_index - get repp_v1_contacts_path - response_json = JSON.parse(response.body, symbolize_names: true) + def test_returns_registrar_contacts + get repp_v1_contacts_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) - puts response_json + assert_response :ok - assert response_json[:count] == 1 + assert_equal @user.registrar.contacts.count, json[:total_number_of_records] + assert_equal @user.registrar.contacts.count, json[:contacts].length - expected_response = [{ domain_name: @auction.domain, - punycode_domain_name: @auction.domain }] + assert json[:contacts][0].is_a? String + end - assert_equal expected_response, response_json[:auctions] + + def test_returns_detailed_registrar_contacts + get repp_v1_contacts_path(details: true), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.contacts.count, json[:total_number_of_records] + assert_equal @user.registrar.contacts.count, json[:contacts].length + + assert json[:contacts][0].is_a? Hash + end + + def test_respects_limit_in_registrar_contact_list + get repp_v1_contacts_path(details: true, limit: 2), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 2, json[:contacts].length + end + + def test_respects_offset_in_registrar_contact_list + offset = 1 + get repp_v1_contacts_path(details: true, offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.contacts.count - offset), json[:contacts].length end end From 7f1637c8d2d23d11e71af6509acac0405e8ec60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 17:14:23 +0300 Subject: [PATCH 040/172] REPP: Contacts show test --- .../repp/v1/contacts_controller.rb | 2 +- .../list_test.rb} | 6 +-- .../integration/repp/v1/contacts/show_test.rb | 45 +++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) rename test/integration/repp/v1/{contacts_test.rb => contacts/list_test.rb} (90%) create mode 100644 test/integration/repp/v1/contacts/show_test.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 6321f2a40..0b52505b0 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -81,7 +81,7 @@ module Repp def find_contact code = params[:id] - @contact = Epp::Contact.find_by!(code: code) + @contact = Epp::Contact.find_by!(code: code, registrar: current_user.registrar) end def contact_params_with_address diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts/list_test.rb similarity index 90% rename from test/integration/repp/v1/contacts_test.rb rename to test/integration/repp/v1/contacts/list_test.rb index 353cb3758..31c4baaf9 100644 --- a/test/integration/repp/v1/contacts_test.rb +++ b/test/integration/repp/v1/contacts/list_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1ContactsTest < ActionDispatch::IntegrationTest +class ReppV1ContactsListTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -34,7 +34,7 @@ class ReppV1ContactsTest < ActionDispatch::IntegrationTest assert json[:contacts][0].is_a? Hash end - def test_respects_limit_in_registrar_contact_list + def test_respects_limit get repp_v1_contacts_path(details: true, limit: 2), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) @@ -43,7 +43,7 @@ class ReppV1ContactsTest < ActionDispatch::IntegrationTest assert_equal 2, json[:contacts].length end - def test_respects_offset_in_registrar_contact_list + def test_respects_offset offset = 1 get repp_v1_contacts_path(details: true, offset: offset), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) diff --git a/test/integration/repp/v1/contacts/show_test.rb b/test/integration/repp/v1/contacts/show_test.rb new file mode 100644 index 000000000..3fd782cca --- /dev/null +++ b/test/integration/repp/v1/contacts/show_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class ReppV1ContactsShowTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_error_when_not_found + get repp_v1_contact_path(id: 'definitelynotexistant'), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_shows_existing_contact + contact = @user.registrar.contacts.first + + get repp_v1_contact_path(id: contact.code), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal contact.code, json[:data][:code] + end + + def test_can_not_access_out_of_scope_contacts + # Contact of registrar goodnames, we're using bestnames API credentials + contact = contacts(:jack) + + get repp_v1_contact_path(id: contact.code), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end +end From 39d09e5543d2e82f8819e9fc57be66a590a1344a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 10:44:48 +0300 Subject: [PATCH 041/172] REPP: Contact check test --- .../repp/v1/contacts/check_test.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/integration/repp/v1/contacts/check_test.rb diff --git a/test/integration/repp/v1/contacts/check_test.rb b/test/integration/repp/v1/contacts/check_test.rb new file mode 100644 index 000000000..be0d979b1 --- /dev/null +++ b/test/integration/repp/v1/contacts/check_test.rb @@ -0,0 +1,30 @@ +require 'test_helper' + +class ReppV1ContactsCheckTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_code_based_check_returns_true_for_available_contact + get '/repp/v1/contacts/check/nonexistant:code', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 'nonexistant:code', json[:data][:contact][:id] + assert_equal true, json[:data][:contact][:available] + end + + def test_code_based_check_returns_true_for_available_contact + contact = contacts(:jack) + get "/repp/v1/contacts/check/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal contact.code, json[:data][:contact][:id] + assert_equal false, json[:data][:contact][:available] + end +end From 9cfa1c2989b80a5fbf4a552297c4c238d7aea1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 11:26:32 +0300 Subject: [PATCH 042/172] REPP: Contact create test --- app/controllers/repp/v1/base_controller.rb | 2 +- .../repp/v1/contacts/create_test.rb | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/contacts/create_test.rb diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index c46c837f6..68cc8d225 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -64,7 +64,7 @@ module Repp @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} render( - json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg], data: data }, + json: { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data }, status: status ) end diff --git a/test/integration/repp/v1/contacts/create_test.rb b/test/integration/repp/v1/contacts/create_test.rb new file mode 100644 index 000000000..30e71eee9 --- /dev/null +++ b/test/integration/repp/v1/contacts/create_test.rb @@ -0,0 +1,127 @@ +require 'test_helper' + +class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_creates_new_contact + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_equal(request_body[:contact][:name], contact.name) + assert_equal(request_body[:contact][:phone], contact.phone) + assert_equal(request_body[:contact][:email], contact.email) + assert_equal(request_body[:contact][:ident][:ident_type], contact.ident_type) + assert_equal(request_body[:contact][:ident][:ident_country_code], contact.ident_country_code) + assert_equal(request_body[:contact][:ident][:ident], contact.ident) + end + + def test_removes_postal_info_when_contact_created + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111111", + "email": "donald@trump.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + }, + "addr": { + "city": "Tallinn", + "street": "Wismari 13", + "zip": "12345", + "country_code": "EE" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1100, json[:code] + assert_equal 'Command completed successfully; Postal address data discarded', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_nil contact.city + assert_nil contact.street + assert_nil contact.zip + assert_nil contact.country_code + end + + def test_requires_contact_address_when_processing_enabled + Setting.address_processing = true + + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2003, json[:code] + assert json[:message].include? 'param is missing or the value is empty' + + Setting.address_processing = false + end + + def test_validates_ident_code + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "123123123" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2005, json[:code] + assert json[:message].include? 'Ident code does not conform to national identification number format' + end +end From 33f6da53c12f5bc18f3ab986a40a5c3e3de0225e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 13:58:21 +0300 Subject: [PATCH 043/172] REPP: Contact update test --- .../repp/v1/contacts_controller.rb | 16 ++-- app/models/actions/contact_update.rb | 2 +- .../repp/v1/contacts/update_test.rb | 82 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 test/integration/repp/v1/contacts/update_test.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 0b52505b0..1677e827f 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -41,7 +41,7 @@ module Repp ## PUT /repp/v1/contacts/1 def update - action = Actions::ContactUpdate.new(@contact, contact_params_with_address, + action = Actions::ContactUpdate.new(@contact, contact_params_with_address(required: false), params[:legal_document], contact_ident_params(required: false), current_user) @@ -84,16 +84,16 @@ module Repp @contact = Epp::Contact.find_by!(code: code, registrar: current_user.registrar) end - def contact_params_with_address - return contact_create_params unless contact_addr_params.key?(:addr) + def contact_params_with_address(required: true) + return contact_create_params(required: required) unless contact_addr_params.key?(:addr) addr = {} contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } - contact_create_params.merge(addr) + contact_create_params(required: required).merge(addr) end - def contact_create_params - params.require(:contact).require(%i[name email phone]) + def contact_create_params(required: true) + params.require(:contact).require(%i[name email phone]) if required params.require(:contact).permit(:name, :email, :phone) end @@ -102,8 +102,10 @@ module Repp params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else - params.permit(ident: %i[ident ident_type ident_country_code]) + params.permit(contact: { ident: %i[ident ident_type ident_country_code] }) end + + params[:contact][:ident] end def contact_addr_params diff --git a/app/models/actions/contact_update.rb b/app/models/actions/contact_update.rb index f8b39ecb4..6400526c5 100644 --- a/app/models/actions/contact_update.rb +++ b/app/models/actions/contact_update.rb @@ -17,7 +17,7 @@ module Actions def call maybe_remove_address maybe_update_statuses - maybe_update_ident + maybe_update_ident if ident maybe_attach_legal_doc commit end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb new file mode 100644 index 000000000..816a90637 --- /dev/null +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -0,0 +1,82 @@ +require 'test_helper' + +class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest + def setup + @contact = contacts(:john) + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_contact + request_body = { + "contact": { + "email": "donaldtrump@yandex.ru" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_equal(request_body[:contact][:email], contact.email) + end + + def test_removes_postal_info_when_updated + request_body = { + "contact": { + "addr": { + "city": "Tallinn", + "street": "Wismari 13", + "zip": "12345", + "country_code": "EE" + } + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1100, json[:code] + assert_equal 'Command completed successfully; Postal address data discarded', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_nil contact.city + assert_nil contact.street + assert_nil contact.zip + assert_nil contact.country_code + end + + def test_can_not_change_ident_code + request_body = { + "contact": { + "name": "Donald Trumpster", + "ident": { + "ident_type": "priv", + "ident_country_code": "US", + "ident": "12345" + } + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + @contact.reload + assert_not @contact.ident == 12345 + assert_response :bad_request + assert_equal 2308, json[:code] + assert json[:message].include? 'Ident update is not allowed. Consider creating new contact object' + end +end From ddc26b81b15cd293bf7d9080f49fdb0d6c6a7ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 15:36:40 +0300 Subject: [PATCH 044/172] REPP:: Contact create/update legaldoc test --- .../repp/v1/contacts_controller.rb | 7 +---- .../repp/v1/contacts/create_test.rb | 29 +++++++++++++++++++ .../repp/v1/contacts/update_test.rb | 22 ++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 1677e827f..0e1bd1791 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -28,7 +28,7 @@ module Repp ## POST /repp/v1/contacts def create @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) - action = Actions::ContactCreate.new(@contact, params[:legal_documents], + action = Actions::ContactCreate.new(@contact, params[:legal_document], contact_ident_params) unless action.call @@ -116,11 +116,6 @@ module Repp params.require(:contact).permit(addr: %i[country_code city street zip]) end end - - def legal_document_params - params.require(:legal_document).require(%i[body type]) - params.require(:legal_document).permit(:body, :type) - end end end end diff --git a/test/integration/repp/v1/contacts/create_test.rb b/test/integration/repp/v1/contacts/create_test.rb index 30e71eee9..f30bc368f 100644 --- a/test/integration/repp/v1/contacts/create_test.rb +++ b/test/integration/repp/v1/contacts/create_test.rb @@ -124,4 +124,33 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest assert_equal 2005, json[:code] assert json[:message].include? 'Ident code does not conform to national identification number format' end + + def test_attaches_legaldoc_if_present + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + }, + }, + "legal_document": { + "type": "pdf", + "body": "#{'test' * 2000}" + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.legal_documents.any? + end end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb index 816a90637..dd762e341 100644 --- a/test/integration/repp/v1/contacts/update_test.rb +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -79,4 +79,26 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest assert_equal 2308, json[:code] assert json[:message].include? 'Ident update is not allowed. Consider creating new contact object' end + + def test_attaches_legaldoc_if_present + request_body = { + "contact": { + "email": "donaldtrump@yandex.ru" + }, + "legal_document": { + "type": "pdf", + "body": "#{'test' * 2000}" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + @contact.reload + assert @contact.legal_documents.any? + end end From 516b2180cab76a28b8552a4c8a132b9df9bd29a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 17:00:25 +0300 Subject: [PATCH 045/172] REPP: Extend V1 base test --- app/controllers/repp/v1/base_controller.rb | 16 ++++------ test/integration/repp/v1/base_test.rb | 37 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 68cc8d225..678ae0a22 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -69,10 +69,6 @@ module Repp ) end - def ip_whitelisted? - return false unless current_user.registrar.api_ip_white?(request.ip) - end - def basic_token pattern = /^Basic / header = request.headers['Authorization'] @@ -95,16 +91,16 @@ module Repp end def check_ip_restriction - ip_restriction = Authorization::RestrictedIP.new(request.ip) - allowed = ip_restriction.can_access_registrar_area?(@current_user.registrar) + allowed = @current_user.registrar.api_ip_white?(request.ip) return if allowed render( - status: :unauthorized, - json: { errors: [ - { base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }, - ] } + json: { + code: 2202, + message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip), + }, + status: :unauthorized ) end diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb index 63255dd24..d0baed30e 100644 --- a/test/integration/repp/v1/base_test.rb +++ b/test/integration/repp/v1/base_test.rb @@ -15,6 +15,15 @@ class ReppV1BaseTest < ActionDispatch::IntegrationTest assert_response :unauthorized assert_equal 'Invalid authorization information', response_json[:message] + + invalid_token = Base64.encode64("nonexistant:user") + headers = { 'Authorization' => "Basic #{invalid_token}" } + + get repp_v1_contacts_path, headers: headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] end def test_authenticates_valid_user @@ -23,4 +32,32 @@ class ReppV1BaseTest < ActionDispatch::IntegrationTest assert_response :ok end + + def test_processes_invalid_base64_token_format_properly + token = '??as8d9sf kjsdjh klsdfjjf' + headers = { 'Authorization' => "Basic #{token}"} + get repp_v1_contacts_path, headers: headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] + end + + def test_takes_ip_whitelist_into_account + Setting.api_ip_whitelist_enabled = true + Setting.registrar_ip_whitelist_enabled = true + + whiteip = white_ips(:one) + whiteip.update(ipv4: '1.1.1.1') + + get repp_v1_contacts_path, headers: @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 2202, response_json[:code] + assert response_json[:message].include? 'Access denied from IP' + + Setting.api_ip_whitelist_enabled = false + Setting.registrar_ip_whitelist_enabled = false + end end From f9bef781ab00a21cb5b08dc03156290ecf7828a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 11:54:25 +0300 Subject: [PATCH 046/172] REPP: Add contact:destroy action --- app/controllers/epp/contacts_controller.rb | 9 ++-- .../repp/v1/contacts_controller.rb | 12 ++++- app/models/actions/contact_delete.rb | 41 ++++++++++++++++ app/models/contact.rb | 25 ---------- .../repp/v1/contacts/delete_test.rb | 47 +++++++++++++++++++ 5 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 app/models/actions/contact_delete.rb create mode 100644 test/integration/repp/v1/contacts/delete_test.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 4ef8d11d3..85305213b 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -44,12 +44,13 @@ module Epp def delete authorize! :delete, @contact, @password - - if @contact.destroy_and_clean(params[:parsed_frame]) - render_epp_response '/epp/contacts/delete' - else + action = Actions::ContactDelete.new(@contact, params[:legal_document]) + unless action.call handle_errors(@contact) + return end + + render_epp_response '/epp/contacts/delete' end def renew diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 0e1bd1791..eea6767f7 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,7 +1,7 @@ module Repp module V1 class ContactsController < BaseController - before_action :find_contact, only: %i[show update] + before_action :find_contact, only: %i[show update destroy] ## GET /repp/v1/contacts def index @@ -53,6 +53,16 @@ module Repp render_success(create_update_success_body) end + def destroy + action = Actions::ContactDelete.new(@contact, params[:legal_document]) + unless action.call + handle_errors(@contact) + return + end + + render_success + end + def contact_addr_present? return false unless contact_addr_params.key?(:addr) diff --git a/app/models/actions/contact_delete.rb b/app/models/actions/contact_delete.rb new file mode 100644 index 000000000..59032d566 --- /dev/null +++ b/app/models/actions/contact_delete.rb @@ -0,0 +1,41 @@ +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 + + 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 diff --git a/app/models/contact.rb b/app/models/contact.rb index 9dc1e34a2..8a154c50c 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -333,31 +333,6 @@ class Contact < ApplicationRecord Country.new(country_code) end - # TODO: refactor, it should not allow to destroy with normal destroy, - # no need separate method - # should use only in transaction - def destroy_and_clean frame - if linked? - errors.add(:domains, :exist) - return false - end - - legal_document_data = ::Deserializers::Xml::LegalDocument.new(frame).call - - if legal_document_data - - doc = LegalDocument.create( - documentable_type: Contact, - document_type: legal_document_data[:type], - body: legal_document_data[:body] - ) - self.legal_documents = [doc] - self.legal_document_id = doc.id - self.save - end - destroy - end - def to_upcase_country_code self.ident_country_code = ident_country_code.upcase if ident_country_code self.country_code = country_code.upcase if country_code diff --git a/test/integration/repp/v1/contacts/delete_test.rb b/test/integration/repp/v1/contacts/delete_test.rb new file mode 100644 index 000000000..07438d8af --- /dev/null +++ b/test/integration/repp/v1/contacts/delete_test.rb @@ -0,0 +1,47 @@ +require 'test_helper' + +class ReppV1ContactsDeleteTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_deletes_unassociated_contact + contact = contacts(:invalid_email) + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + end + + def test_can_not_delete_associated_contact + contact = contacts(:john) + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2305, json[:code] + assert_equal 'Object association prohibits operation [domains]', json[:message] + end + + def test_handles_unknown_contact + delete "/repp/v1/contacts/definitely:unexistant", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + end + + def test_can_not_destroy_other_registrar_contact + contact = contacts(:jack) + + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + end +end From 2a1967918cec277ec85889e7eb1ec3fe1b4617a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 12:31:51 +0300 Subject: [PATCH 047/172] REPP: Domains list test --- test/integration/repp/v1/domains/list_test.rb | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/integration/repp/v1/domains/list_test.rb diff --git a/test/integration/repp/v1/domains/list_test.rb b/test/integration/repp/v1/domains/list_test.rb new file mode 100644 index 000000000..bee390e5c --- /dev/null +++ b/test/integration/repp/v1/domains/list_test.rb @@ -0,0 +1,54 @@ +require 'test_helper' + +class ReppV1DomainsListTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_registrar_domains + get repp_v1_domains_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:domains].length + + assert json[:data][:domains][0].is_a? String + end + + def test_returns_detailed_registrar_domains + get repp_v1_domains_path(details: true), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:domains].length + + assert json[:data][:domains][0].is_a? Hash + end + + def test_respects_limit + get repp_v1_domains_path(details: true, limit: 2), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 2, json[:data][:domains].length + end + + def test_respects_offset + offset = 1 + get repp_v1_domains_path(details: true, offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.domains.count - offset), json[:data][:domains].length + end +end From 9a36b0403ce8e32baa89f2fb03738e0ed2b73807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 13:10:28 +0300 Subject: [PATCH 048/172] REPP: Domain transfer info test --- app/controllers/repp/v1/domains_controller.rb | 2 +- .../repp/v1/domains/transfer_info_test.rb | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/domains/transfer_info_test.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 163731c69..652786488 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -90,7 +90,7 @@ module Repp return if @domain.transfer_code.eql?(request.headers['Auth-Code']) - @epp_errors << { code: '401', msg: I18n.t('errors.messages.epp_authorization_error') } + @epp_errors << { code: 2202, msg: I18n.t('errors.messages.epp_authorization_error') } handle_errors end diff --git a/test/integration/repp/v1/domains/transfer_info_test.rb b/test/integration/repp/v1/domains/transfer_info_test.rb new file mode 100644 index 000000000..6c64e24b9 --- /dev/null +++ b/test/integration/repp/v1/domains/transfer_info_test.rb @@ -0,0 +1,40 @@ +require 'test_helper' + +class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + @domain = domains(:shop) + @auth_headers = { 'Authorization' => token } + end + + def test_can_query_domain_info + headers = @auth_headers + headers['Auth-Code'] = @domain.transfer_code + + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.name, json[:data][:domain] + assert json[:data][:registrant].present? + assert json[:data][:admin_contacts].present? + assert json[:data][:tech_contacts].present? + end + + def test_respects_domain_authorization_code + headers = @auth_headers + headers['Auth-Code'] = 'jhfgifhdg' + + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2202, json[:code] + assert_equal 'Authorization error', json[:message] + assert_empty json[:data] + end +end From 774ada6ebb30f9cf7d1875ca9295ce23d2182beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 14:00:16 +0300 Subject: [PATCH 049/172] REPP: Domain transfer test --- app/controllers/repp/v1/domains_controller.rb | 3 +- .../repp/v1/domains/transfer_info_test.rb | 4 +- .../repp/v1/domains/transfer_test.rb | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/integration/repp/v1/domains/transfer_test.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 652786488..da2990ead 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -29,7 +29,7 @@ module Repp @errors ||= [] @successful = [] - params[:data][:domain_transfers].each do |transfer| + transfer_params[:domain_transfers].each do |transfer| initiate_transfer(transfer) end @@ -57,6 +57,7 @@ module Repp add_error("#{domain_name} transfer code is wrong") return end + domain end diff --git a/test/integration/repp/v1/domains/transfer_info_test.rb b/test/integration/repp/v1/domains/transfer_info_test.rb index 6c64e24b9..f57675b06 100644 --- a/test/integration/repp/v1/domains/transfer_info_test.rb +++ b/test/integration/repp/v1/domains/transfer_info_test.rb @@ -13,7 +13,7 @@ class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest headers = @auth_headers headers['Auth-Code'] = @domain.transfer_code - get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -29,7 +29,7 @@ class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest headers = @auth_headers headers['Auth-Code'] = 'jhfgifhdg' - get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: headers json = JSON.parse(response.body, symbolize_names: true) assert_response :bad_request diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb new file mode 100644 index 000000000..4c1280b47 --- /dev/null +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + @domain = domains(:hospital) + + @auth_headers = { 'Authorization' => token } + end + + def test_transfers_domain + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal @domain.name, json[:data][0][:attributes][:domain_name] + assert_equal 'domain_transfer', json[:data][0][:type] + end + + def test_does_not_transfer_domain_with_invalid_auth_code + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": "sdfgsdfg" } + ] + } + } + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'Command failed', json[:message] + + assert_equal "#{@domain.name} transfer code is wrong", json[:data][0][:title] + end +end From 06ad8c8c77f16daa2eb4fa96cb11ef12694770a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 15:14:33 +0300 Subject: [PATCH 050/172] REPP: Domain tech contact replace test --- .../repp/v1/domains/contacts_controller.rb | 2 +- app/controllers/repp/v1/domains_controller.rb | 6 +- .../v1/domains/contact_replacement_test.rb | 65 +++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 test/integration/repp/v1/domains/contact_replacement_test.rb diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 70f64ed28..fb5dfb567 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -7,7 +7,7 @@ module Repp def set_current_contact @current_contact = current_user.registrar.contacts.find_by!( - code: params[:current_contact_id] + code: contact_params[:current_contact_id] ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index da2990ead..7612b7d75 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -6,7 +6,7 @@ module Repp def index records = current_user.registrar.domains domains = records.limit(limit).offset(offset) - domains = domains.pluck(:name) unless params[:details] == 'true' + domains = domains.pluck(:name) unless index_params[:details] == 'true' render_success(data: { domains: domains, total_number_of_records: records.count }) end @@ -96,11 +96,11 @@ module Repp end def limit - params[:limit] || 200 + index_params[:limit] || 200 end def offset - params[:offset] || 0 + index_params[:offset] || 0 end def index_params diff --git a/test/integration/repp/v1/domains/contact_replacement_test.rb b/test/integration/repp/v1/domains/contact_replacement_test.rb new file mode 100644 index 000000000..3cbd9eb8e --- /dev/null +++ b/test/integration/repp/v1/domains/contact_replacement_test.rb @@ -0,0 +1,65 @@ +require 'test_helper' + +class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_replaces_tech_contact_with_new_one + replaceable_contact = contacts(:william) + replacing_contact = contacts(:jane) + + payload = { + "current_contact_id": replaceable_contact.code, + "new_contact_id": replacing_contact.code + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert json[:data][:affected_domains].include? 'airport.test' + assert json[:data][:affected_domains].include? 'shop.test' + + assert_empty json[:data][:skipped_domains] + end + + def test_tech_contact_id_must_differ + replaceable_contact = contacts(:william) + replacing_contact = contacts(:william) + + payload = { + "current_contact_id": replaceable_contact.code, + "new_contact_id": replacing_contact.code + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'New contact must be different from current', json[:message] + end + + def test_contact_codes_must_be_valid + payload = { + "current_contact_id": 'dfgsdfg', + "new_contact_id": 'vvv' + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + +end From a43df5ca95610f5b89ec262270147e415f0d4a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 15:34:16 +0300 Subject: [PATCH 051/172] REPP: Accounts balance test --- .../repp/v1/accounts_controller.rb | 2 +- .../repp/v1/accounts/balance_test.rb | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/accounts/balance_test.rb diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb index 5adf99ea1..89c14808f 100644 --- a/app/controllers/repp/v1/accounts_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -4,7 +4,7 @@ module Repp def balance resp = { balance: current_user.registrar.cash_account.balance, currency: current_user.registrar.cash_account.currency } - render(json: resp, status: :ok) + render_success(data: resp) end end end diff --git a/test/integration/repp/v1/accounts/balance_test.rb b/test/integration/repp/v1/accounts/balance_test.rb new file mode 100644 index 000000000..785e0aee8 --- /dev/null +++ b/test/integration/repp/v1/accounts/balance_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class ReppV1BalanceTest < ActionDispatch::IntegrationTest + def setup + @registrar = users(:api_bestnames) + token = Base64.encode64("#{@registrar.username}:#{@registrar.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_can_query_balance + get '/repp/v1/accounts/balance', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @registrar.registrar.cash_account.balance.to_s, json[:data][:balance] + assert_equal @registrar.registrar.cash_account.currency, json[:data][:currency] + end +end From e9c0d09b9fabebce56e9dee171ed023570f0849c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 16:05:26 +0300 Subject: [PATCH 052/172] REPP: Registrar nameserver replacement test --- .../repp/v1/registrar/nameservers_test.rb | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/integration/repp/v1/registrar/nameservers_test.rb diff --git a/test/integration/repp/v1/registrar/nameservers_test.rb b/test/integration/repp/v1/registrar/nameservers_test.rb new file mode 100644 index 000000000..f01769dfb --- /dev/null +++ b/test/integration/repp/v1/registrar/nameservers_test.rb @@ -0,0 +1,77 @@ +require 'test_helper' + +class ReppV1RegistrarNameserversTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_nameserver_values + nameserver = nameservers(:shop_ns1) + payload = { + "data": { + "id": nameserver.hostname, + "type": "nameserver", + "attributes": { + "hostname": "#{nameserver.hostname}.test", + "ipv4": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal({ hostname: "#{nameserver.hostname}.test", ipv4: ["1.1.1.1"] }, json[:data][:attributes]) + assert_equal({ hostname: "#{nameserver.hostname}.test", ipv4: ["1.1.1.1"] }, json[:data][:attributes]) + assert json[:data][:affected_domains].include? 'airport.test' + assert json[:data][:affected_domains].include? 'shop.test' + end + + def test_nameserver_with_hostname_must_exist + payload = { + "data": { + "id": 'ns.nonexistant.test', + "type": "nameserver", + "attributes": { + "hostname": "ns1.dn.test", + "ipv4": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_ip_must_be_in_correct_format + nameserver = nameservers(:shop_ns1) + payload = { + "data": { + "id": nameserver.hostname, + "type": "nameserver", + "attributes": { + "hostname": "#{nameserver.hostname}.test", + "ipv6": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2005, json[:code] + assert_equal 'IPv6 is invalid [ipv6]', json[:message] + end +end From 03d940a6950f55f3c64afa1d60b50bd93ed7af64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 15:25:02 +0300 Subject: [PATCH 053/172] REPP: Fix bulk domain transfer --- app/controllers/repp/v1/domains_controller.rb | 31 ++------ app/models/actions/domain_transfer.rb | 77 +++++++++++++++++++ .../repp/v1/domains/transfer_test.rb | 15 ++-- 3 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 app/models/actions/domain_transfer.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 7612b7d75..7e81fac55 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,37 +33,16 @@ module Repp initiate_transfer(transfer) end - if @errors.any? - render_epp_error(:bad_request, @errors) - else - render_success(data: @successful) - end + render_success(data: {success: @successful, failed: @errors}) end def initiate_transfer(transfer) - domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - return unless domain + domain = Epp::Domain.find_or_initialize_by(name: transfer[:domain_name]) + action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], current_user.registrar) - DomainTransfer.request(domain, current_user.registrar) - @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } - end + @successful << { type: 'domain_transfer', domain_name: domain.name } and return if action.call - def transferable_domain(domain_name, transfer_code) - domain = Domain.find_by(name: domain_name) - # rubocop:disable Style/AndOr - add_error("#{domain_name} does not exist") and return unless domain - # rubocop:enable Style/AndOr - unless domain.transfer_code.eql?(transfer_code) - add_error("#{domain_name} transfer code is wrong") - return - end - - domain - end - - def add_error(msg) - @errors ||= [] - @errors << { title: msg } + @errors << { type: 'domain_transfer', domain_name: domain.name, errors: domain.errors[:epp_errors] } end private diff --git a/app/models/actions/domain_transfer.rb b/app/models/actions/domain_transfer.rb new file mode 100644 index 000000000..620e9b4db --- /dev/null +++ b/app/models/actions/domain_transfer.rb @@ -0,0 +1,77 @@ +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 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 + return unless validate_transfer_code + return unless validate_registrar + return unless validate_eligilibty + return unless validate_not_discarded + + true + end + + def validate_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 true unless user == domain.registrar + + domain.add_epp_error('2002', nil, nil, I18n.t(:domain_already_belongs_to_the_querying_registrar)) + false + end + + def validate_eligilibty + return true unless domain.non_transferable? + + domain.add_epp_error('2304', nil, nil, 'Domain is not transferable??') + false + end + + def validate_not_discarded + return true unless domain.discarded? + + domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer') + false + end + + def commit + bare_domain = Domain.find(domain.id) + ::DomainTransfer.request(bare_domain, user) + end + end +end diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb index 4c1280b47..9d2518117 100644 --- a/test/integration/repp/v1/domains/transfer_test.rb +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -25,8 +25,11 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert_equal @domain.name, json[:data][0][:attributes][:domain_name] - assert_equal 'domain_transfer', json[:data][0][:type] + assert_equal @domain.name, json[:data][:success][0][:domain_name] + + @domain.reload + + assert @domain.registrar = @user.registrar end def test_does_not_transfer_domain_with_invalid_auth_code @@ -40,10 +43,10 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload json = JSON.parse(response.body, symbolize_names: true) - assert_response :bad_request - assert_equal 2304, json[:code] - assert_equal 'Command failed', json[:message] + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] - assert_equal "#{@domain.name} transfer code is wrong", json[:data][0][:title] + assert_equal "Invalid authorization information", json[:data][:failed][0][:errors][0][:msg] end end From f2983da14cce0935d406e13b2a6e9ae47b88576f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 15:31:37 +0300 Subject: [PATCH 054/172] REPP: Fix CC issues --- app/controllers/repp/v1/domains_controller.rb | 14 +++++--- app/models/actions/domain_transfer.rb | 33 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 7e81fac55..ba90f23f2 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,16 +33,20 @@ module Repp initiate_transfer(transfer) end - render_success(data: {success: @successful, failed: @errors}) + render_success(data: { success: @successful, failed: @errors }) end def initiate_transfer(transfer) domain = Epp::Domain.find_or_initialize_by(name: transfer[:domain_name]) - action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], current_user.registrar) + action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], + current_user.registrar) - @successful << { type: 'domain_transfer', domain_name: domain.name } and return if action.call - - @errors << { type: 'domain_transfer', domain_name: domain.name, errors: domain.errors[:epp_errors] } + if action.call + @successful << { type: 'domain_transfer', domain_name: domain.name } + else + @errors << { type: 'domain_transfer', domain_name: domain.name, + errors: domain.errors[:epp_errors] } + end end private diff --git a/app/models/actions/domain_transfer.rb b/app/models/actions/domain_transfer.rb index 620e9b4db..1ff9aafe9 100644 --- a/app/models/actions/domain_transfer.rb +++ b/app/models/actions/domain_transfer.rb @@ -14,10 +14,12 @@ module Actions def call return unless domain_exists? - return unless run_validations + return unless valid_transfer_code? - #return domain.pending_transfer if domain.pending_transfer - #attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call) + 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? @@ -33,15 +35,12 @@ module Actions end def run_validations - return unless validate_transfer_code - return unless validate_registrar - return unless validate_eligilibty - return unless validate_not_discarded - - true + validate_registrar + validate_eligilibty + validate_not_discarded end - def validate_transfer_code + def valid_transfer_code? return true if transfer_code == domain.transfer_code domain.add_epp_error('2202', nil, nil, 'Invalid authorization information') @@ -49,24 +48,22 @@ module Actions end def validate_registrar - return true unless user == domain.registrar + return unless user == domain.registrar - domain.add_epp_error('2002', nil, nil, I18n.t(:domain_already_belongs_to_the_querying_registrar)) - false + domain.add_epp_error('2002', nil, nil, + I18n.t(:domain_already_belongs_to_the_querying_registrar)) end def validate_eligilibty - return true unless domain.non_transferable? + return unless domain.non_transferable? - domain.add_epp_error('2304', nil, nil, 'Domain is not transferable??') - false + domain.add_epp_error('2304', nil, nil, 'Object status prohibits operation') end def validate_not_discarded - return true unless domain.discarded? + return unless domain.discarded? domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer') - false end def commit From 8790d1415a3e3c1f2abfce2aecab82a7fc78eab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 16:18:07 +0300 Subject: [PATCH 055/172] REPP: Extend domain transfer test --- test/integration/api/domain_transfers_test.rb | 43 ----------- .../repp/v1/domains/transfer_test.rb | 75 +++++++++++++++++++ 2 files changed, 75 insertions(+), 43 deletions(-) diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index ecb69d262..3e9c10100 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -12,26 +12,6 @@ class APIDomainTransfersTest < ApplicationIntegrationTest Setting.transfer_wait_time = @original_transfer_wait_time end - def test_returns_domain_transfers - post '/repp/v1/domains/transfer', params: request_params, as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 200 - - expected_body = { - code: 1000, - message: 'Command completed successfully', - data: [ - { - type: 'domain_transfer', - attributes: { domain_name: 'shop.test' }, - } - ] - } - - real_body = JSON.parse(response.body, symbolize_names: true) - assert_equal(expected_body, real_body) - end - def test_creates_new_domain_transfer assert_difference -> { @domain.transfers.size } do post '/repp/v1/domains/transfer', params: request_params, as: :json, @@ -83,29 +63,6 @@ class APIDomainTransfersTest < ApplicationIntegrationTest assert_equal 1, @new_registrar.contacts.where(name: 'William').size end - def test_fails_if_domain_does_not_exist - post '/repp/v1/domains/transfer', - params: { data: { domain_transfers: [{ domain_name: 'non-existent.test', - transfer_code: 'any' }] } }, - as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 400 - assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'non-existent.test does not exist' }] }), - JSON.parse(response.body, symbolize_names: true) - end - - def test_fails_if_transfer_code_is_wrong - post '/repp/v1/domains/transfer', - params: { data: { domain_transfers: [{ domain_name: 'shop.test', - transfer_code: 'wrong' }] } }, - as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 400 - refute_equal @new_registrar, @domain.registrar - assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'shop.test transfer code is wrong' }] }), - JSON.parse(response.body, symbolize_names: true) - end - private def request_params diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb index 9d2518117..46d5c30c4 100644 --- a/test/integration/repp/v1/domains/transfer_test.rb +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -32,6 +32,31 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert @domain.registrar = @user.registrar end + def test_does_not_transfer_domain_if_not_transferable + @domain.schedule_force_delete(type: :fast_track) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Object status prohibits operation', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert_not @domain.registrar == @user.registrar + end + def test_does_not_transfer_domain_with_invalid_auth_code payload = { "data": { @@ -49,4 +74,54 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert_equal "Invalid authorization information", json[:data][:failed][0][:errors][0][:msg] end + + def test_does_not_transfer_domain_to_same_registrar + @domain.update!(registrar: @user.registrar) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Domain already belongs to the querying registrar', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert @domain.registrar == @user.registrar + end + + def test_does_not_transfer_domain_if_discarded + @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Object is not eligible for transfer', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert_not @domain.registrar == @user.registrar + end end From 9503850d71c0ec24f1201334715f7b9f05ea8913 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 26 Oct 2020 14:00:19 +0500 Subject: [PATCH 056/172] Remove extra day from soft delete years Closes #1720 --- app/models/concerns/domain/force_delete.rb | 2 +- test/models/domain/force_delete_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index af3aaa7c7..9b75d1e92 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -106,7 +106,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def soft_delete_dates(years) - self.force_delete_start = valid_to - years.years + 1.day + self.force_delete_start = valid_to - years.years self.force_delete_date = force_delete_start + Setting.expire_warning_period.days + Setting.redemption_grace_period.days end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index ad91ccfec..0969504a4 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -27,8 +27,8 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase @domain.reload assert @domain.force_delete_scheduled? - assert_equal Date.parse('2010-09-20'), @domain.force_delete_date.to_date - assert_equal Date.parse('2010-08-06'), @domain.force_delete_start.to_date + assert_equal Date.parse('2010-09-19'), @domain.force_delete_date.to_date + assert_equal Date.parse('2010-08-05'), @domain.force_delete_start.to_date end def test_schedules_force_delete_soft_less_than_year_ahead From cfd8e8b7f130cadfdbe0f8e72ef368fd20dbe4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 12:13:15 +0200 Subject: [PATCH 057/172] Docs: Update REPP account --- doc/repp/v1/account.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/repp/v1/account.md b/doc/repp/v1/account.md index 511c51382..80afabcdb 100644 --- a/doc/repp/v1/account.md +++ b/doc/repp/v1/account.md @@ -19,7 +19,11 @@ Content-Length: 37 Content-Type: application/json { - "balance": "324.45", - "currency": "EUR" + "code": 1000, + "message": "Command completed successfully", + "data": { + "balance": "356.0", + "currency": "EUR" + } } ``` From 4e895d9124bc24bd48cce643f0a46a55e8ed6e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 12:59:04 +0200 Subject: [PATCH 058/172] Docs: Update REPP domain --- doc/repp/v1/domain.md | 204 ++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 95 deletions(-) diff --git a/doc/repp/v1/domain.md b/doc/repp/v1/domain.md index c6734cbe2..20607f3b9 100644 --- a/doc/repp/v1/domain.md +++ b/doc/repp/v1/domain.md @@ -25,44 +25,52 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 808 Content-Type: application/json { - "domains": [ - { - "id": 1, - "name": "domain0.ee", - "registrar_id": 2, - "registered_at": "2015-09-09T09:11:14.861Z", - "status": null, - "valid_from": "2015-09-09T09:11:14.861Z", - "valid_to": "2016-09-09T09:11:14.861Z", - "registrant_id": 1, - "transfer_code": "98oiewslkfkd", - "created_at": "2015-09-09T09:11:14.861Z", - "updated_at": "2015-09-09T09:11:14.860Z", - "name_dirty": "domain0.ee", - "name_puny": "domain0.ee", - "period": 1, - "period_unit": "y", - "creator_str": null, - "updator_str": null, - "outzone_at": "2016-09-24T09:11:14.861Z", - "delete_date": "2016-10-24", - "registrant_verification_asked_at": null, - "registrant_verification_token": null, - "pending_json": { - }, - "force_delete_date": null, - "statuses": [ - "ok" - ], - "status_notes": { + "code": 1000, + "message": "Command completed successfully", + "data": { + "domains": [ + { + "id": 7, + "name": "private.ee", + "registrar_id": 2, + "valid_to": "2022-09-23T00:00:00.000+03:00", + "registrant_id": 11, + "created_at": "2020-09-22T14:16:47.420+03:00", + "updated_at": "2020-10-21T13:31:43.733+03:00", + "name_dirty": "private.ee", + "name_puny": "private.ee", + "period": 1, + "period_unit": "y", + "creator_str": "2-ApiUser: test", + "updator_str": null, + "outzone_at": null, + "delete_date": null, + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": {}, + "force_delete_date": null, + "statuses": [ + "serverRenewProhibited" + ], + "status_notes": { + "ok": "", + "serverRenewProhibited": "" + }, + "upid": null, + "up_date": null, + "uuid": "6b6affa7-1449-4bd8-acf5-8b4752406705", + "locked_by_registrant_at": null, + "force_delete_start": null, + "force_delete_data": null, + "auth_info": "367b1e6d1f0d9aa190971ad8f571cd4d", + "valid_from": "2020-09-22T14:16:47.420+03:00" } - } - ], - "total_number_of_records": 2 + ], + "total_number_of_records": 10 + } } ``` @@ -83,14 +91,17 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 54 Content-Type: application/json { - "domains": [ - "domain1.ee" - ], - "total_number_of_records": 2 + "code": 1000, + "message": "Command completed successfully", + "data": { + "domains": [ + "private.ee", + ], + "total_number_of_records": 1 + } } ``` @@ -117,65 +128,68 @@ Please note that domain transfer/authorisation code must be placed in header - * ``` HTTP/1.1 200 OK Cache-Control: max-age=0, private, must-revalidate -Content-Length: 784 Content-Type: application/json - { - "domain":"ee-test.ee", - "registrant":{ - "code":"EE:R1", - "name":"Registrant", - "ident":"17612535", - "ident_type":"org", - "ident_country_code":"EE", - "phone":"+372.1234567", - "email":"registrant@cache.ee", - "street":"Businesstreet 1", - "city":"Tallinn", - "zip":"10101", - "country_code":"EE", - "statuses":[ - "ok", - "linked" - ] - }, - "admin_contacts":[ - { - "code":"EE:A1", - "name":"Admin Contact", - "ident":"17612535376", - "ident_type":"priv", - "ident_country_code":"EE", - "phone":"+372.7654321", - "email":"admin@cache.ee", - "street":"Adminstreet 2", - "city":"Tallinn", - "zip":"12345", - "country_code":"EE", - "statuses":[ - "ok", - "linked" + "code": 1000, + "message": "Command completed successfully", + "data": { + "domain":"ee-test.ee", + "registrant":{ + "code":"EE:R1", + "name":"Registrant", + "ident":"17612535", + "ident_type":"org", + "ident_country_code":"EE", + "phone":"+372.1234567", + "email":"registrant@cache.ee", + "street":"Businesstreet 1", + "city":"Tallinn", + "zip":"10101", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + }, + "admin_contacts":[ + { + "code":"EE:A1", + "name":"Admin Contact", + "ident":"17612535376", + "ident_type":"priv", + "ident_country_code":"EE", + "phone":"+372.7654321", + "email":"admin@cache.ee", + "street":"Adminstreet 2", + "city":"Tallinn", + "zip":"12345", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + } + ], + "tech_contacts":[ + { + "code":"EE:T1", + "name":"Tech Contact", + "ident":"17612536", + "ident_type":"org", + "ident_country_code":"EE", + "phone":"+372.7654321", + "email":"tech@cache.ee", + "street":"Techstreet 1", + "city":"Tallinn", + "zip":"12345", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + } ] } - ], - "tech_contacts":[ - { - "code":"EE:T1", - "name":"Tech Contact", - "ident":"17612536", - "ident_type":"org", - "ident_country_code":"EE", - "phone":"+372.7654321", - "email":"tech@cache.ee", - "street":"Techstreet 1", - "city":"Tallinn", - "zip":"12345", - "country_code":"EE", - "statuses":[ - "ok", - "linked" - ] - } - ] + } } ``` From 09c62fd7ebb1e13f9c4eb71c34425eb38f373f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 13:40:11 +0200 Subject: [PATCH 059/172] Docs: Update REPP domain transfer --- doc/repp/v1/domain_transfers.md | 70 ++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/doc/repp/v1/domain_transfers.md b/doc/repp/v1/domain_transfers.md index a6eb4683c..a11c0a852 100644 --- a/doc/repp/v1/domain_transfers.md +++ b/doc/repp/v1/domain_transfers.md @@ -1,28 +1,28 @@ # Domain transfers -## POST /repp/v1/domain_transfers +## POST /repp/v1/domains/transfer Transfers domains. #### Request ``` -POST /repp/v1/domain_transfers +POST /repp/v1/domains/transfer Accept: application/json Content-Type: application/json Authorization: Basic dGVzdDp0ZXN0dGVzdA== { - "data":{ - "domainTransfers":[ - { - "domainName":"example.com", - "transferCode":"63e7" - }, - { - "domainName":"example.org", - "transferCode":"15f9" - } - ] - } + "data": { + "domain_transfers": [ + { + "domain_name":"example.com", + "transferCode":"63e7" + }, + { + "domain_name":"example.org", + "transferCode":"15f9" + } + ] + } } ``` @@ -31,14 +31,21 @@ Authorization: Basic dGVzdDp0ZXN0dGVzdA== HTTP/1.1 200 Content-Type: application/json { - "data":[ + "code": 1000, + "message": "Command completed successfully", + "data": { + "success": [ { - "type":"domain_transfer" + "type": "domain_transfer", + "domain_name": "example.com" }, { - "type":"domain_transfer" + "type": "domain_transfer", + "domain_name": "example.org" } - ] + ], + "failed": [] + } } ``` @@ -48,13 +55,32 @@ Content-Type: application/json HTTP/1.1 400 Content-Type: application/json { - "errors":[ + "code": 1000, + "message": "Command completed successfully", + "data": { + "success": [], + "failed": [ { - "title":"example.com transfer code is wrong" + "type": "domain_transfer", + "domain_name": "example.com", + "errors": [ + { + "code": "2202", + "msg": "Invalid authorization information" + } + ] }, { - "title":"example.org does not exist" + "type": "domain_transfer", + "domain_name": "example.org", + "errors": [ + { + "code": "2304", + "msg": "Object status prohibits operation" + } + ] } - ] + ] + } } ``` From 909460668de0052b5fcbcdeeddf44dd7844055ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 14:35:47 +0200 Subject: [PATCH 060/172] Docs: REPP Domain tech contact replace --- doc/repp/v1/domain_contacts.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/repp/v1/domain_contacts.md b/doc/repp/v1/domain_contacts.md index b412e9f73..2e542bf81 100644 --- a/doc/repp/v1/domain_contacts.md +++ b/doc/repp/v1/domain_contacts.md @@ -5,15 +5,26 @@ Replaces all domain contacts of the current registrar. ### Example request ``` -$ curl https://repp.internet.ee/v1/domains/contacts \ - -X PATCH \ - -u username:password \ - -d current_contact_id=foo \ - -d new_contact_id=bar +PATCH /repp/v1/domains/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 ``` { - "affected_domains": ["example.com", "example.org"] + "code": 1000, + "message": "Command completed successfully", + "data": { + "affected_domains": [ + "private.ee", + ], + "skipped_domains": [] + } } ``` From d2d7bfb3862cd3397f72131042d9ccf20d93062c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 14:55:30 +0200 Subject: [PATCH 061/172] Docs: REPP registrar nameserver replacement --- doc/repp/v1/nameservers.md | 50 +++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/doc/repp/v1/nameservers.md b/doc/repp/v1/nameservers.md index 8190530d7..ab53c72df 100644 --- a/doc/repp/v1/nameservers.md +++ b/doc/repp/v1/nameservers.md @@ -10,15 +10,15 @@ Accept: application/json Content-Type: application/json Authorization: Basic dGVzdDp0ZXN0dGVzdA== { - "data":{ - "type": "nameserver", - "id": "ns1.example.com", - "attributes": { - "hostname": "new-ns1.example.com", - "ipv4": ["192.0.2.1", "192.0.2.2"], - "ipv6": ["2001:db8::1", "2001:db8::2"] - }, + "data": { + "type": "nameserver", + "id": "ns1.example.com", + "attributes": { + "hostname": "new-ns1.example.com", + "ipv4": ["192.0.2.1", "192.0.2.2"], + "ipv6": ["2001:db8::1", "2001:db8::2"] } + } } ``` @@ -27,16 +27,26 @@ Authorization: Basic dGVzdDp0ZXN0dGVzdA== HTTP/1.1 200 Content-Type: application/json { - "data":{ + "code": 1000, + "message": "Command completed successfully", + "data": { "type": "nameserver", "id": "new-ns1.example.com", "attributes": { "hostname": "new-ns1.example.com", - "ipv4": ["192.0.2.1", "192.0.2.2"], - "ipv6": ["2001:db8::1", "2001:db8::2"] - } - }, - "affected_domains": ["example.com", "example.org"] + "ipv4": [ + "192.0.2.1", + "192.0.2.2" + ], + "ipv6": [ + "2001:db8::1", + "2001:db8::2" + ] + }, + "affected_domains": [ + "private.ee" + ] + } } ``` @@ -44,14 +54,10 @@ Content-Type: application/json ``` HTTP/1.1 400 Content-Type: application/json + { - "errors":[ - { - "title":"ns1.example.com does not exist" - }, - { - "title":"192.0.2.1 is not a valid IPv4 address" - } - ] + "code": 2005, + "message": "IPv4 is invalid [ipv4]", + "data": {} } ``` From 7fd31b7004ee3d6ec68e6d4d9f169b4623f140eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 15:12:40 +0200 Subject: [PATCH 062/172] Docs: REPP contact management --- doc/repp/v1/contact.md | 138 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/doc/repp/v1/contact.md b/doc/repp/v1/contact.md index 41f45551f..aa6904b72 100644 --- a/doc/repp/v1/contact.md +++ b/doc/repp/v1/contact.md @@ -88,3 +88,141 @@ Content-Type: application/json "total_number_of_records": 2 } ``` + +## POST /repp/v1/contacts +Creates new contact + + +#### Request +``` +POST /repp/v1/contacts HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json + +{ + "contact": { + "name": "John Doe", + "email": "john@doe.com", + "phone": "+371.1234567", + "ident": { + "ident": "12345678901", + "ident_type": "priv", + "ident_country_code": "EE" + } + } +} +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": { + "contact": { + "id": "ATSAA:20DCDCA1" + } + } +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2005, + "message": "Ident code does not conform to national identification number format of Estonia", + "data": {} +} +``` + +## PUT /repp/v1/contacts/**contact id** +Updates existing contact + + +#### Request +``` +PUT /repp/v1/contacts/ATSAA:9CD5F321 HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json + +{ + "contact": { + "phone": "+372.123123123" + } +} +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": { + "contact": { + "id": "ATSAA:20DCDCA1" + } + } +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2005, + "message": "Phone nr is invalid [phone]", + "data": {} +} +``` + +## DELETE /repp/v1/contacts/**contact id** +Deletes existing contact + + +#### Request +``` +DELETE /repp/v1/contacts/ATSAA:9CD5F321 HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": {} +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2305, + "message": "Object association prohibits operation [domains]", + "data": {} +} +``` From 8bc064a0b80abe8b190def7f9bbeea98f311b056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 17:09:12 +0200 Subject: [PATCH 063/172] Fix contact create with predefined code --- app/controllers/repp/v1/contacts_controller.rb | 2 +- app/models/epp/contact.rb | 2 +- lib/deserializers/xml/contact.rb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index eea6767f7..a6cd7d07d 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -104,7 +104,7 @@ module Repp def contact_create_params(required: true) params.require(:contact).require(%i[name email phone]) if required - params.require(:contact).permit(:name, :email, :phone) + params.require(:contact).permit(:name, :email, :phone, :code) end def contact_ident_params(required: true) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 50ebac065..3f0f3e8ab 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -36,7 +36,7 @@ class Epp::Contact < Contact attrs = epp ? attrs_from(frame, new_record: true) : frame super( attrs.merge( - code: epp ? frame.css('id').text : frame[:id], + code: epp ? frame.css('id').text : frame[:code], registrar: registrar ) ) diff --git a/lib/deserializers/xml/contact.rb b/lib/deserializers/xml/contact.rb index 4dd29c683..7c8404916 100644 --- a/lib/deserializers/xml/contact.rb +++ b/lib/deserializers/xml/contact.rb @@ -14,6 +14,7 @@ module Deserializers email: if_present('email'), fax: if_present('fax'), phone: if_present('voice'), + id: if_present('id'), # Address fields city: if_present('postalInfo addr city'), From 461b7c1d9c4eb9a180db665833ae8f221f0feefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 27 Oct 2020 12:28:35 +0200 Subject: [PATCH 064/172] Fix EPP contact update --- app/models/actions/contact_create.rb | 1 + lib/deserializers/xml/contact.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 13162a553..22fabc7b9 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -71,6 +71,7 @@ module Actions end def commit + contact.id = nil # new record return false if @error contact.generate_code diff --git a/lib/deserializers/xml/contact.rb b/lib/deserializers/xml/contact.rb index 7c8404916..4dd29c683 100644 --- a/lib/deserializers/xml/contact.rb +++ b/lib/deserializers/xml/contact.rb @@ -14,7 +14,6 @@ module Deserializers email: if_present('email'), fax: if_present('fax'), phone: if_present('voice'), - id: if_present('id'), # Address fields city: if_present('postalInfo addr city'), From 407577cdb7924673f453af4cf3d15ccd3bb35f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 27 Oct 2020 13:00:34 +0200 Subject: [PATCH 065/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e2523dc..a2609918e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +27.10.2020 +* Fixed 1 day delay in force delete for multi year registrations [#1720](https://github.com/internetee/registry/issues/1720) + 20.10.2020 * ForceDelete mailer now respects option to not notify registrant [#1719](https://github.com/internetee/registry/pull/1719) From b1903fa8d68e47fbda220a7889b8fa2d722812ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 28 Oct 2020 15:13:17 +0200 Subject: [PATCH 066/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2609918e..90926de81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +28.10.2020 +* domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) + 27.10.2020 * Fixed 1 day delay in force delete for multi year registrations [#1720](https://github.com/internetee/registry/issues/1720) From 083be0d536769993d73f0385660d61cfb8798ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 28 Oct 2020 16:20:08 +0200 Subject: [PATCH 067/172] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90926de81..4b8504cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 28.10.2020 -* domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) +* Domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) +* Added multi-language support to whois disclaimer [#1703](https://github.com/internetee/registry/issues/1703) 27.10.2020 * Fixed 1 day delay in force delete for multi year registrations [#1720](https://github.com/internetee/registry/issues/1720) From c1bba784d6450948c4f809bf805051b749372de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 28 Oct 2020 16:35:35 +0200 Subject: [PATCH 068/172] Update contact(s) name based on eIdentity data from Registrant API --- app/models/registrant_user.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index c0addb5cd..40f48f69c 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -2,6 +2,7 @@ class RegistrantUser < User attr_accessor :idc_data devise :trackable, :timeoutable + after_save :update_related_contacts def ability @ability ||= Ability.new(self) @@ -54,6 +55,12 @@ class RegistrantUser < User username.split.second end + def update_related_contacts + cc, idcode = registrant_ident.split('-') + contacts = Contact.where(ident: idcode, country_code: cc).where('name != ?', username) + contacts.each { |c| c.update(name: username) } + end + class << self def find_or_create_by_api_data(user_data = {}) return false unless user_data[:ident] From 7b258f8e798ada9d681177ba6e0d1c9e60abbae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 28 Oct 2020 17:05:48 +0200 Subject: [PATCH 069/172] Don't force RegistrantUser name to be uppercase --- app/models/registrant_user.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index 40f48f69c..cb8212e17 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -57,7 +57,9 @@ class RegistrantUser < User def update_related_contacts cc, idcode = registrant_ident.split('-') - contacts = Contact.where(ident: idcode, country_code: cc).where('name != ?', username) + contacts = Contact.where(ident: idcode, country_code: cc) + .where('UPPER(name) != UPPER(?)', username) + contacts.each { |c| c.update(name: username) } end @@ -67,8 +69,8 @@ class RegistrantUser < User return false unless user_data[:first_name] return false unless user_data[:last_name] - user_data.each_value { |v| v.upcase! if v.is_a?(String) } user_data[:country_code] ||= 'EE' + %i[ident country_code].each { |f| user_data[f].upcase! if user_data[f].is_a?(String) } find_or_create_by_user_data(user_data) end From e1d2fb45d57be67ae924cf47767cc31b253d7624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 29 Oct 2020 11:11:39 +0200 Subject: [PATCH 070/172] Fix tests --- app/models/registrant_user.rb | 5 ++-- .../registrant_user_creation_test.rb | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index cb8212e17..420446582 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -2,7 +2,6 @@ class RegistrantUser < User attr_accessor :idc_data devise :trackable, :timeoutable - after_save :update_related_contacts def ability @ability ||= Ability.new(self) @@ -56,8 +55,7 @@ class RegistrantUser < User end def update_related_contacts - cc, idcode = registrant_ident.split('-') - contacts = Contact.where(ident: idcode, country_code: cc) + contacts = Contact.where(ident: ident, country_code: country_code) .where('UPPER(name) != UPPER(?)', username) contacts.each { |c| c.update(name: username) } @@ -100,6 +98,7 @@ class RegistrantUser < User user.username = "#{user_data[:first_name]} #{user_data[:last_name]}" user.save + user.update_related_contacts user end end diff --git a/test/models/registrant_user/registrant_user_creation_test.rb b/test/models/registrant_user/registrant_user_creation_test.rb index 5ed680795..9fff4ca02 100644 --- a/test/models/registrant_user/registrant_user_creation_test.rb +++ b/test/models/registrant_user/registrant_user_creation_test.rb @@ -14,7 +14,7 @@ class RegistrantUserCreationTest < ActiveSupport::TestCase assert_equal('JOHN SMITH', user.username) end - def test_find_or_create_by_api_data_creates_a_user_after_upcasing_input + def test_find_or_create_by_api_data_creates_a_user_with_original_name user_data = { ident: '37710100070', first_name: 'John', @@ -24,6 +24,25 @@ class RegistrantUserCreationTest < ActiveSupport::TestCase RegistrantUser.find_or_create_by_api_data(user_data) user = User.find_by(registrant_ident: 'EE-37710100070') - assert_equal('JOHN SMITH', user.username) + assert_equal('John Smith', user.username) + end + + def test_updates_related_contacts_name_if_differs_from_e_identity + contact = contacts(:john) + contact.update(ident: '39708290276', ident_country_code: 'EE') + + user_data = { + ident: '39708290276', + first_name: 'John', + last_name: 'Doe' + } + + RegistrantUser.find_or_create_by_api_data(user_data) + + user = User.find_by(registrant_ident: 'EE-39708290276') + assert_equal('John Doe', user.username) + + contact.reload + assert_equal user.username, contact.name end end From 44637b08cf46ec8ecee7cef366e4c19994f807fb Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 29 Oct 2020 16:15:12 +0500 Subject: [PATCH 071/172] Fix statuses search bug --- app/controllers/registrar/domains_controller.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index 50ad0bd10..581b517ae 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -16,13 +16,14 @@ class Registrar end end - if params[:statuses_contains] - domains = current_registrar_user.registrar.domains.includes(:registrar, :registrant).where( - "statuses @> ?::varchar[]", "{#{params[:statuses_contains].join(',')}}" - ) - else - domains = current_registrar_user.registrar.domains.includes(:registrar, :registrant) - end + domains = if params[:statuses_contains] + current_registrar_user.registrar.domains + .includes(:registrar, :registrant) + .where('domains.statuses @> ?::varchar[]', + "{#{params[:statuses_contains].join(',')}}") + else + current_registrar_user.registrar.domains.includes(:registrar, :registrant) + end normalize_search_parameters do @q = domains.search(search_params) From 358a39e0fdecc410face333569c538eadbe836c2 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 29 Oct 2020 17:08:34 +0500 Subject: [PATCH 072/172] Fix search via Ransack association --- .../registrar/domains_controller.rb | 24 +++++++++++-------- .../registrar/domains/_search_form.html.erb | 3 ++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index 581b517ae..1a46da463 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -11,27 +11,27 @@ class Registrar search_params[:name_matches].present? domain = Domain.find_by(name: search_params[:name_matches]) - if domain - redirect_to info_registrar_domains_url(domain_name: domain.name) and return - end + redirect_to info_registrar_domains_url(domain_name: domain.name) and return if domain end domains = if params[:statuses_contains] - current_registrar_user.registrar.domains - .includes(:registrar, :registrant) - .where('domains.statuses @> ?::varchar[]', - "{#{params[:statuses_contains].join(',')}}") + current_domain_scope.where('domains.statuses @> ?::varchar[]', + "{#{params[:statuses_contains].join(',')}}") else - current_registrar_user.registrar.domains.includes(:registrar, :registrant) + current_domain_scope end + if params[:contacts_ident_eq] + domains = domains.where(contacts: { ident: params[:contacts_ident_eq] }) + end + normalize_search_parameters do - @q = domains.search(search_params) + @q = domains.search(search_params.except(:contacts_ident_eq)) @domains = @q.result.page(params[:page]) # if we do not get any results, add wildcards to the name field and search again if @domains.count == 0 && search_params[:name_matches] !~ /^%.+%$/ - new_search_params = search_params.to_h + new_search_params = search_params.to_h.except(:contacts_ident_eq) new_search_params[:name_matches] = "%#{new_search_params[:name_matches]}%" @q = domains.search(new_search_params) @domains = @q.result.page(params[:page]) @@ -57,6 +57,10 @@ class Registrar end end + def current_domain_scope + current_registrar_user.registrar.domains.includes(:registrar, :registrant) + end + def info authorize! :info, Depp::Domain @data = @domain.info(params[:domain_name]) if params[:domain_name] diff --git a/app/views/registrar/domains/_search_form.html.erb b/app/views/registrar/domains/_search_form.html.erb index 08b370b76..e9e5b5e1a 100644 --- a/app/views/registrar/domains/_search_form.html.erb +++ b/app/views/registrar/domains/_search_form.html.erb @@ -18,7 +18,8 @@
<%= f.label :contact_ident, for: nil %> - <%= f.search_field :contacts_ident_eq, class: 'form-control', placeholder: t(:contact_ident) %> + <%= f.search_field :contacts_ident_eq, value: search_params[:contacts_ident_eq], + class: 'form-control', placeholder: t(:contact_ident) %>
From 2f259982da9e1d22a80305c37e80fd529bf59ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 30 Oct 2020 11:51:20 +0200 Subject: [PATCH 073/172] Find related contacts via ident and ident_country_code --- app/models/registrant_user.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index 420446582..f1e0e1c2d 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -55,7 +55,8 @@ class RegistrantUser < User end def update_related_contacts - contacts = Contact.where(ident: ident, country_code: country_code) + cc, idcode = registrant_ident.split('-') + contacts = Contact.where(ident: idcode, ident_country_code: cc) .where('UPPER(name) != UPPER(?)', username) contacts.each { |c| c.update(name: username) } From 89154c4fe200482caa1f868802b8bdf642b13cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 30 Oct 2020 11:56:07 +0200 Subject: [PATCH 074/172] Use predefined methods to determine country and ident --- app/models/registrant_user.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index f1e0e1c2d..089292917 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -55,8 +55,7 @@ class RegistrantUser < User end def update_related_contacts - cc, idcode = registrant_ident.split('-') - contacts = Contact.where(ident: idcode, ident_country_code: cc) + contacts = Contact.where(ident: ident, ident_country_code: country.alpha2) .where('UPPER(name) != UPPER(?)', username) contacts.each { |c| c.update(name: username) } From 7d6d53e4207df8e62ac390beb7a519444d578f2f Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 2 Nov 2020 15:47:00 +0500 Subject: [PATCH 075/172] Auto-select email template Chooses template based on domain registrant ident_type. Closes #442 --- .../admin/domains/force_delete_controller.rb | 8 +++++-- .../domains/_force_delete_dialog.html.erb | 6 ----- app/views/admin/domains/edit.html.erb | 2 +- .../admin_area/domains/force_delete_test.rb | 22 +++++++++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index c61f050d2..e64012de7 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -22,7 +22,7 @@ module Admin send_email domain.update(contact_notification_sent_date: Time.zone.today) else - domain.update(template_name: params[:template_name]) + domain.update(template_name: template_name) end end @@ -34,6 +34,10 @@ module Admin private + def template_name + domain.registrant.org? ? 'legal_person' : 'private_person' + end + def domain @domain ||= Domain.find(params[:domain_id]) end @@ -46,7 +50,7 @@ module Admin DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, registrant: domain.registrant, - template_name: params[:template_name]).deliver_now + template_name: template_name).deliver_now end def force_delete_type diff --git a/app/views/admin/domains/_force_delete_dialog.html.erb b/app/views/admin/domains/_force_delete_dialog.html.erb index a76c14edd..932a3f75c 100644 --- a/app/views/admin/domains/_force_delete_dialog.html.erb +++ b/app/views/admin/domains/_force_delete_dialog.html.erb @@ -33,12 +33,6 @@ - <% end %> <%= render 'form' %> -<%= render 'force_delete_dialog', domain: @domain, templates: force_delete_templates %> +<%= render 'force_delete_dialog', domain: @domain %> diff --git a/test/system/admin_area/domains/force_delete_test.rb b/test/system/admin_area/domains/force_delete_test.rb index 4ccc10923..6aa53be6c 100644 --- a/test/system/admin_area/domains/force_delete_test.rb +++ b/test/system/admin_area/domains/force_delete_test.rb @@ -42,6 +42,22 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase find(:css, '#soft_delete').set(true) click_link_or_button 'Force delete domain' end + + @domain.reload + assert_equal template_name, @domain.template_name + end + + def test_uses_legal_template_if_registrant_org + @domain.registrant.update(ident_type: 'org') + + assert_emails 0 do + visit edit_admin_domain_url(@domain) + find(:css, '#soft_delete').set(true) + click_link_or_button 'Force delete domain' + end + + @domain.reload + assert_equal template_name, @domain.template_name end def test_allows_to_skip_notifying_registrant_and_admin_contacts_by_email @@ -71,4 +87,10 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase assert_no_button 'Schedule force delete' assert_no_link 'Schedule force delete' end + + private + + def template_name + @domain.registrant.org? ? 'legal_person' : 'private_person' + end end From 269c5c284ff216d143b52c24fa56ee28d7657317 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 2 Nov 2020 16:05:04 +0500 Subject: [PATCH 076/172] Remove ForceDelete from statuses blocking confirmation --- app/models/domain.rb | 2 +- test/models/domain/force_delete_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/domain.rb b/app/models/domain.rb index 2b3213a36..648a7b3c5 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -384,7 +384,7 @@ class Domain < ApplicationRecord end def registrant_update_confirmable?(token) - return false if (statuses & [DomainStatus::FORCE_DELETE, DomainStatus::DELETE_CANDIDATE]).any? + return false if statuses.include? DomainStatus::DELETE_CANDIDATE return false unless pending_update? return false unless registrant_verification_asked? return false unless registrant_verification_token == token diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 0969504a4..9743989ac 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -252,4 +252,16 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase assert @domain.force_delete_scheduled? assert @domain.pending_update? end + + def test_force_delete_does_not_affect_registrant_update_confirmable + @domain.schedule_force_delete(type: :soft) + @domain.registrant_verification_asked!('test', User.last.id) + @domain.save! + @domain.reload + + @domain.statuses << DomainStatus::PENDING_UPDATE + + assert @domain.force_delete_scheduled? + assert @domain.registrant_update_confirmable?(@domain.registrant_verification_token) + end end From b1488169492827fc0180942b3ec71e66023b8280 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 3 Nov 2020 14:22:12 +0500 Subject: [PATCH 077/172] Fix ForceDelete possible statuses doubling --- app/models/concerns/domain/force_delete.rb | 6 +++--- test/models/domain/force_delete_test.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 9b75d1e92..7a1ace6a7 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -128,9 +128,9 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def add_force_delete_statuses - statuses << DomainStatus::FORCE_DELETE - statuses << DomainStatus::SERVER_RENEW_PROHIBITED - statuses << DomainStatus::SERVER_TRANSFER_PROHIBITED + self.statuses |= [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED] end def remove_force_delete_statuses diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 0969504a4..4678ed99a 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -137,6 +137,19 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase assert_not @domain.force_delete_scheduled? end + def test_force_delete_does_not_double_statuses + statuses = [ + DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED, + ] + @domain.statuses = @domain.statuses + statuses + @domain.save! + @domain.reload + @domain.schedule_force_delete(type: :fast_track) + assert_equal @domain.statuses.size, statuses.size + end + def test_cancelling_force_delete_removes_statuses_that_were_set_on_force_delete statuses = [ DomainStatus::FORCE_DELETE, From 3444c4e021e007ad75e384f64cb5c0922ec39cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 3 Nov 2020 16:14:01 +0200 Subject: [PATCH 078/172] Add registrar poll message after updateing registrant contact(s) names --- app/models/registrant_user.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index 089292917..aa41ccff8 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -58,7 +58,11 @@ class RegistrantUser < User contacts = Contact.where(ident: ident, ident_country_code: country.alpha2) .where('UPPER(name) != UPPER(?)', username) - contacts.each { |c| c.update(name: username) } + contacts.each do |contact| + contact.update(name: username) + action = actions.create!(contact: contact, operation: :update) + contact.registrar.notify(action) + end end class << self From 08815f58124e8439259d54b36990b4f2f9c67298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 3 Nov 2020 18:07:20 +0200 Subject: [PATCH 079/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b8504cde..3860062c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +03.11.2020 +* Fixed registrant confirmation while forcedelete is set on a domain [#1729](https://github.com/internetee/registry/issues/1729) + 28.10.2020 * Domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) * Added multi-language support to whois disclaimer [#1703](https://github.com/internetee/registry/issues/1703) From b62ffc4e7ee8079c694e166da564dc2099600958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 3 Nov 2020 18:18:47 +0200 Subject: [PATCH 080/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3860062c8..0d92e9339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 03.11.2020 * Fixed registrant confirmation while forcedelete is set on a domain [#1729](https://github.com/internetee/registry/issues/1729) +* Fixed search in registrar domain view [#262](https://github.com/internetee/registry/issues/262) 28.10.2020 * Domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) From 433445f1ac22101cb830d96a8528f86253df344f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 3 Nov 2020 18:36:35 +0200 Subject: [PATCH 081/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d92e9339..2eafe8711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 03.11.2020 * Fixed registrant confirmation while forcedelete is set on a domain [#1729](https://github.com/internetee/registry/issues/1729) * Fixed search in registrar domain view [#262](https://github.com/internetee/registry/issues/262) +* Fixed double status issue on setting forceDelete [#1135](https://github.com/internetee/registry/issues/1135) 28.10.2020 * Domain renew now canceles pending delete process [#1664](https://github.com/internetee/registry/issues/1664) From 20d5ab9ebf1fe4fca67531e02fe46e82444af04c Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 4 Nov 2020 14:24:09 +0500 Subject: [PATCH 082/172] Fix statuses processing for ForceDelete --- app/models/concerns/domain/force_delete.rb | 4 +-- test/models/domain/force_delete_test.rb | 33 ++++++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 7a1ace6a7..7f3cb3922 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -80,8 +80,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def cancel_force_delete - restore_statuses_before_force_delete remove_force_delete_statuses + restore_statuses_before_force_delete clear_force_delete_data self.force_delete_date = nil self.force_delete_start = nil @@ -119,7 +119,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def preserve_current_statuses_for_force_delete - self.statuses_before_force_delete = statuses.clone + update(statuses_before_force_delete: statuses) end def restore_statuses_before_force_delete diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 8fcbfeb2b..f0723c326 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -150,19 +150,36 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase assert_equal @domain.statuses.size, statuses.size end - def test_cancelling_force_delete_removes_statuses_that_were_set_on_force_delete - statuses = [ - DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED, - ] - @domain.statuses = @domain.statuses + statuses + def test_cancelling_force_delete_removes_force_delete_status @domain.schedule_force_delete(type: :fast_track) + assert @domain.statuses.include?(DomainStatus::FORCE_DELETE) + assert @domain.statuses.include?(DomainStatus::SERVER_RENEW_PROHIBITED) + assert @domain.statuses.include?(DomainStatus::SERVER_TRANSFER_PROHIBITED) + @domain.cancel_force_delete @domain.reload - assert_empty @domain.statuses & statuses + assert_not @domain.statuses.include?(DomainStatus::FORCE_DELETE) + assert_not @domain.statuses.include?(DomainStatus::SERVER_RENEW_PROHIBITED) + assert_not @domain.statuses.include?(DomainStatus::SERVER_TRANSFER_PROHIBITED) + end + + def test_cancelling_force_delete_keeps_previous_statuses + statuses = [ + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED, + ] + + @domain.statuses = statuses + @domain.save! + @domain.reload + + @domain.schedule_force_delete(type: :fast_track) + @domain.cancel_force_delete + @domain.reload + + assert_equal @domain.statuses, statuses end def test_hard_force_delete_should_have_outzone_and_purge_date_with_time From a6702267e58482dc56a812e71a2b730ecdb3fb24 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 4 Nov 2020 17:11:46 +0500 Subject: [PATCH 083/172] Move template decision method to FD concern --- app/controllers/admin/domains/force_delete_controller.rb | 8 ++------ app/models/concerns/domain/force_delete.rb | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index e64012de7..6a111926f 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -22,7 +22,7 @@ module Admin send_email domain.update(contact_notification_sent_date: Time.zone.today) else - domain.update(template_name: template_name) + domain.update(template_name: domain.notification_template) end end @@ -34,10 +34,6 @@ module Admin private - def template_name - domain.registrant.org? ? 'legal_person' : 'private_person' - end - def domain @domain ||= Domain.find(params[:domain_id]) end @@ -50,7 +46,7 @@ module Admin DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, registrant: domain.registrant, - template_name: template_name).deliver_now + template_name: domain.notification_template).deliver_now end def force_delete_type diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 9b75d1e92..bf619c65f 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -19,6 +19,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end end + def notification_template + registrant.org? ? 'legal_person' : 'private_person' + end + def force_delete_scheduled? statuses.include?(DomainStatus::FORCE_DELETE) end From b35eb160a6ecf32c3f4735b72f7b29eb43ed29e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 4 Nov 2020 16:28:09 +0200 Subject: [PATCH 084/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eafe8711..6721791d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +04.11.2020 +* Email notification templates for forceDelete are now automatically selected according to registrant type [#442](https://github.com/internetee/registry/issues/442) + 03.11.2020 * Fixed registrant confirmation while forcedelete is set on a domain [#1729](https://github.com/internetee/registry/issues/1729) * Fixed search in registrar domain view [#262](https://github.com/internetee/registry/issues/262) From c3f63ed43a80a60410e52e4e29d53af89c136008 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 30 Oct 2020 15:36:34 +0500 Subject: [PATCH 085/172] Add new mailer template for expired soft delete domains --- app/jobs/domain_expire_email_job.rb | 6 ++- app/mailers/domain_expire_mailer.rb | 37 ++++++++++---- .../expired_soft.html.erb | 48 +++++++++++++++++++ .../expired_soft.text.erb | 48 +++++++++++++++++++ test/mailers/domain_expire_mailer_test.rb | 11 ++++- .../previews/domain_expire_mailer_preview.rb | 8 +++- 6 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 app/views/mailers/domain_expire_mailer/expired_soft.html.erb create mode 100644 app/views/mailers/domain_expire_mailer/expired_soft.text.erb diff --git a/app/jobs/domain_expire_email_job.rb b/app/jobs/domain_expire_email_job.rb index 9b70a54e6..94bd8670c 100644 --- a/app/jobs/domain_expire_email_job.rb +++ b/app/jobs/domain_expire_email_job.rb @@ -4,6 +4,10 @@ class DomainExpireEmailJob < Que::Job return if domain.registered? - DomainExpireMailer.expired(domain: domain, registrar: domain.registrar).deliver_now + if domain.force_delete_scheduled? + DomainExpireMailer.expired_soft(domain: domain, registrar: domain.registrar).deliver_now + else + DomainExpireMailer.expired(domain: domain, registrar: domain.registrar).deliver_now + end end end diff --git a/app/mailers/domain_expire_mailer.rb b/app/mailers/domain_expire_mailer.rb index ecbd8ee3d..e73b1fa84 100644 --- a/app/mailers/domain_expire_mailer.rb +++ b/app/mailers/domain_expire_mailer.rb @@ -1,19 +1,38 @@ class DomainExpireMailer < ApplicationMailer + attr_accessor :domain, :registrar + def expired(domain:, registrar:) - @domain = domain_presenter(domain: domain) - @registrar = registrar_presenter(registrar: registrar) + process_mail(domain: domain, registrar: registrar, method_name: __method__.to_s) + end - recipient = filter_invalid_emails(emails: domain.primary_contact_emails, domain: domain) - subject = default_i18n_subject(domain_name: domain.name) - - logger.info("Send DomainExpireMailer#expired email for domain #{domain.name} (##{domain.id})" \ - " to #{recipient.join(', ')}") - - mail(to: recipient, subject: subject) + def expired_soft(domain:, registrar:) + process_mail(domain: domain, registrar: registrar, method_name: __method__.to_s) end private + def process_mail(domain:, registrar:, method_name:) + init(domain, registrar) + + logger.info("Send DomainExpireMailer##{method_name} email for #{domain.name} (##{domain.id})" \ + " to #{recipient(domain).join(', ')}") + + mail(to: recipient(domain), subject: subject) + end + + def init(domain, registrar) + @domain = domain_presenter(domain: domain) + @registrar = registrar_presenter(registrar: registrar) + end + + def recipient(domain) + filter_invalid_emails(emails: domain.primary_contact_emails, domain: @domain) + end + + def subject + default_i18n_subject(domain_name: @domain.name) + end + def domain_presenter(domain:) DomainPresenter.new(domain: domain, view: view_context) end diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.html.erb b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb new file mode 100644 index 000000000..1dd661a38 --- /dev/null +++ b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb @@ -0,0 +1,48 @@ +

Domeen <%= @domain.name %> on aegunud ning suunatud kustutusmenetlusse kuna oleme tuvastanud domeeniga seotud kontaktides olulisi puudusi.

+ +

Lugupeetud .ee domeeni registreerija/halduskontakt

+ +

Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole.

+ +

<%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit.

+ +

Domeeni <%= @domain.name %> registripidaja:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +

Ülevaate kõikidest endaga seotud domeenidest saate registreerija portaalist.

+ +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Domain <%= @domain.name %> has expired

+ +

Dear registrant/administrative contact of .ee domain,

+ +

The domain name <%= @domain.name %> has expired and since <%= @domain.on_hold_date %> is no longer available on the Internet. Domain registration has invalid contact data. Renewal and registrar transfer is therefore prohibited until contact data has been fixed. To correct the data and renew your domain registration, please contact your registrar.

+ +

If you do not renew the <%= @domain.name %> domain registration, it is deleted and put on auction to .ee domain auction environment at <%= @domain.delete_date %>. Read more about .ee domain auctions here.

+ +

Registrar of the <%= @domain.name %>:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +

You can find an overview of all your domains at the registrant's portal.

+ +<%= render 'mailers/shared/signatures/signature.en.html' %> + +
+ +

Срок действия домена <%= @domain.name %> истек

+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Срок действия доменного имени <%= @domain.name %> истек, и с <%= @domain.on_hold_date %> оно больше не доступно в интернете. У домена указаны неверные контактные данные. Обновление и перенос к другому регистратору заблокированы до исправления контактных данных. Для исправления контактных данных и обновления регистрации вашего домена, пожалуйста, обратитесь в вашему регистратору.

+ +

Если доменное имя не продлено, домен <%= @domain.name %> будет удален и <%= @domain.delete_date %> идет на аукцион в .ee среду аукциона. О проведении доменных аукционов читайте здесь.

+ +

Pегистратор домена <%= @domain.name %>:

+<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %> + +

Обзор всех связанных с вами доменов можете получить на портале регистратора.

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.text.erb b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb new file mode 100644 index 000000000..0e6d9c953 --- /dev/null +++ b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb @@ -0,0 +1,48 @@ +Domeen <%= @domain.name %> on aegunud ning suunatud kustutusmenetlusse kuna oleme tuvastanud domeeniga seotud kontaktides olulisi puudusi. + +Lugupeetud .ee domeeni registreerija/halduskontakt + +Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole. + +<%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit https://www.internet.ee/domeenioksjonid. + +Domeeni <%= @domain.name %> registripidaja: +<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +Ülevaate kõikidest endaga seotud domeenidest saate registreerija portaalist https://registrant.internet.ee/registrant/. + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +-------------------------------------- + +Domain <%= @domain.name %> has expired + +Dear registrant/administrative contact of .ee domain, + +The domain name <%= @domain.name %> has expired and since <%= @domain.on_hold_date %> is no longer available on the Internet. Domain registration has invalid contact data. Renewal and registrar transfer is therefore prohibited until contact data has been fixed. To correct the data and renew your domain registration, please contact your registrar. + +If you do not renew the <%= @domain.name %> domain registration, it is deleted and put on auction to .ee domain auction environment at <%= @domain.delete_date %>. Read more about .ee domain auctions here https://www.internet.ee/domains/auction-environment-user-agreement#3-terms-and-conditions-for-participation-in-the-auction-of-the-auction-environment. + +Registrar of the <%= @domain.name %>: +<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +You can find an overview of all your domains at the registrant's portal https://registrant.internet.ee/registrant/. + +<%= render 'mailers/shared/signatures/signature.en.html' %> + +-------------------------------------- + +Срок действия домена <%= @domain.name %> истек + +Уважаемый регистрант/административный контакт домена .ee + +Срок действия доменного имени <%= @domain.name %> истек, и с <%= @domain.on_hold_date %> оно больше не доступно в интернете. У домена указаны неверные контактные данные. Обновление и перенос к другому регистратору заблокированы до исправления контактных данных. Для исправления контактных данных и обновления регистрации вашего домена, пожалуйста, обратитесь в вашему регистратору. + +Если доменное имя не продлено, домен <%= @domain.name %> будет удален и <%= @domain.delete_date %> идет на аукцион в .ee среду аукциона. О проведении доменных аукционов читайте здесь https://www.internet.ee/domeny/dogovor-pol-zovatelya-aukcionnoj-sredy#3-usloviya-uchastiya-v-aukcione. + +Pегистратор домена <%= @domain.name %>: +<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %> + +Обзор всех связанных с вами доменов можете получить на портале регистратора https://registrant.internet.ee/registrant/. + +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/test/mailers/domain_expire_mailer_test.rb b/test/mailers/domain_expire_mailer_test.rb index 1502bf2f6..9209652fd 100644 --- a/test/mailers/domain_expire_mailer_test.rb +++ b/test/mailers/domain_expire_mailer_test.rb @@ -11,4 +11,13 @@ class DomainExpireMailerTest < ActionMailer::TestCase assert_equal 'Domeen shop.test on aegunud / Domain shop.test has expired' \ ' / Срок действия домена shop.test истек', email.subject end -end \ No newline at end of file + + def test_delivers_domain_expiration_soft_email + domain = domains(:shop) + assert_equal 'shop.test', domain.name + + DomainExpireMailer.expired_soft(domain: domain, registrar: domain.registrar).deliver_now + + assert_emails 1 + end +end diff --git a/test/mailers/previews/domain_expire_mailer_preview.rb b/test/mailers/previews/domain_expire_mailer_preview.rb index bec206c0f..4d66d2fad 100644 --- a/test/mailers/previews/domain_expire_mailer_preview.rb +++ b/test/mailers/previews/domain_expire_mailer_preview.rb @@ -4,4 +4,10 @@ class DomainExpireMailerPreview < ActionMailer::Preview DomainExpireMailer.expired(domain: domain, registrar: domain.registrar) end -end \ No newline at end of file + + def expired_soft + domain = Domain.first + DomainExpireMailer.expired_soft(domain: domain, + registrar: domain.registrar) + end +end From 344da76dc64904ea103bf99966e5fdaa0381cffd Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 11:22:25 +0500 Subject: [PATCH 086/172] Fix email subject --- app/mailers/domain_expire_mailer.rb | 6 +++--- config/locales/mailers/domain_expire.en.yml | 7 ++++++- test/mailers/domain_expire_mailer_test.rb | 8 +++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/mailers/domain_expire_mailer.rb b/app/mailers/domain_expire_mailer.rb index e73b1fa84..229120825 100644 --- a/app/mailers/domain_expire_mailer.rb +++ b/app/mailers/domain_expire_mailer.rb @@ -17,7 +17,7 @@ class DomainExpireMailer < ApplicationMailer logger.info("Send DomainExpireMailer##{method_name} email for #{domain.name} (##{domain.id})" \ " to #{recipient(domain).join(', ')}") - mail(to: recipient(domain), subject: subject) + mail(to: recipient(domain), subject: subject(method_name)) end def init(domain, registrar) @@ -29,8 +29,8 @@ class DomainExpireMailer < ApplicationMailer filter_invalid_emails(emails: domain.primary_contact_emails, domain: @domain) end - def subject - default_i18n_subject(domain_name: @domain.name) + def subject(method_name) + I18n.t("domain_expire_mailer.#{method_name}.subject", domain_name: @domain.name) end def domain_presenter(domain:) diff --git a/config/locales/mailers/domain_expire.en.yml b/config/locales/mailers/domain_expire.en.yml index 9a83a7a32..36353a44e 100644 --- a/config/locales/mailers/domain_expire.en.yml +++ b/config/locales/mailers/domain_expire.en.yml @@ -4,4 +4,9 @@ en: subject: >- Domeen %{domain_name} on aegunud / Domain %{domain_name} has expired - / Срок действия домена %{domain_name} истек \ No newline at end of file + / Срок действия домена %{domain_name} истек + expired_soft: + subject: >- + Domeen %{domain_name} on aegunud ning suunatud kustutusmenetlusse + / Domain %{domain_name} has expired and directed into deletion process + / Срок действия домена %{domain_name} истек diff --git a/test/mailers/domain_expire_mailer_test.rb b/test/mailers/domain_expire_mailer_test.rb index 9209652fd..84e520b78 100644 --- a/test/mailers/domain_expire_mailer_test.rb +++ b/test/mailers/domain_expire_mailer_test.rb @@ -8,16 +8,18 @@ class DomainExpireMailerTest < ActionMailer::TestCase email = DomainExpireMailer.expired(domain: domain, registrar: domain.registrar).deliver_now assert_emails 1 - assert_equal 'Domeen shop.test on aegunud / Domain shop.test has expired' \ - ' / Срок действия домена shop.test истек', email.subject + assert_equal I18n.t("domain_expire_mailer.expired.subject", domain_name: domain.name), + email.subject end def test_delivers_domain_expiration_soft_email domain = domains(:shop) assert_equal 'shop.test', domain.name - DomainExpireMailer.expired_soft(domain: domain, registrar: domain.registrar).deliver_now + email = DomainExpireMailer.expired_soft(domain: domain, registrar: domain.registrar).deliver_now assert_emails 1 + assert_equal I18n.t("domain_expire_mailer.expired_soft.subject", domain_name: domain.name), + email.subject end end From ee49ffea0271dd0f302fd1673331714aa0d37f74 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 11:02:26 +0500 Subject: [PATCH 087/172] First version of invalid email template on FD --- .../forced/invalid_email.html.erb | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb new file mode 100644 index 000000000..2dec52905 --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb @@ -0,0 +1,45 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele on saanud teatavaks, et juriidiline isik registrikoodiga <%= @registrant.reg_no %> on äriregistrist kustutatud.

+ +

Kuna äriregistrist kustutatud juriidiline isik ei saa olla domeeni registreerijaks, algas domeeni <%= @domain.name %> suhtes <%= @delete_period_length %> päevane kustutusmenetlus. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Domeeni suhtes õigust omaval isikul on võimalus esitada domeeni <%= @domain.name %> registripidajale <%= @registrar.name %> domeeni üleandmise taotlus koos seda tõendava dokumendiga.

+ +

Kui üleandmine ei ole <%= @delete_period_length %> päeva jooksul toimunud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, otherwise in <%= @delete_period_length %> days the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction portal. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что юридическое лицо с регистрационным кодом <%= @registrant.reg_no %> удалено из коммерческого реестра.

+ +

Поскольку удаленное из коммерческого регистра юридическое лицо не может являться регистрантом домена, начат <%= @delete_period_length %>-дневный процесс удаления домена <%= @domain.name %>. Домен доступен в интернете на протяжении <%= @expire_warning_period %> дней после начала процесса удаления.

+ +

Лицо, обладающее правом на домен, может подать регистратору <%= @registrar.name %> домена <%= @domain.name %> ходатайство о передаче домена, представив вместе с ходатайством подтверждающие документы. Документы должны быть представлены регистратору в течение <%= @delete_period_length %> дней.

+ +

Если передача не состоится в течение <%= @delete_period_length %> дней, <%= @domain.force_delete_date %> домен <%= @domain.name %> отправится на доменный аукцион в аукционной среде .ee. Если в течение 24 часов в отношении домена <%= @domain.name %> не поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». Читайте о других возможных результатах аукциона.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: + <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> From 3ce0fc4a5c3d035b1c9f47675dc4c8279364d562 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 12:18:33 +0500 Subject: [PATCH 088/172] Add invalid email template --- app/models/concerns/email_verifable.rb | 4 ++ app/models/domain.rb | 4 ++ .../forced/invalid_email.html.erb | 24 +++++----- .../forced/invalid_email.text.erb | 47 +++++++++++++++++++ 4 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb diff --git a/app/models/concerns/email_verifable.rb b/app/models/concerns/email_verifable.rb index dc512b2c8..2168a0754 100644 --- a/app/models/concerns/email_verifable.rb +++ b/app/models/concerns/email_verifable.rb @@ -15,6 +15,10 @@ module Concerns domain: domain(billing_email)) end + def email_verification_failed? + email_verification.failed? + end + class_methods do def domain(email) Mail::Address.new(email).domain&.downcase || 'not_found' diff --git a/app/models/domain.rb b/app/models/domain.rb index 648a7b3c5..b15bb7c55 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -642,6 +642,10 @@ class Domain < ApplicationRecord DNS::DomainName.new(name) end + def contact_emails_verification_failed + contacts.select(&:email_verification_failed?)&.map(&:email)&.uniq + end + def self.to_csv CSV.generate do |csv| csv << column_names diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb index 2dec52905..9e69519e4 100644 --- a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb @@ -1,12 +1,12 @@

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

-

Eesti Interneti Sihtasutusele on saanud teatavaks, et juriidiline isik registrikoodiga <%= @registrant.reg_no %> on äriregistrist kustutatud.

+

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>

-

Kuna äriregistrist kustutatud juriidiline isik ei saa olla domeeni registreerijaks, algas domeeni <%= @domain.name %> suhtes <%= @delete_period_length %> päevane kustutusmenetlus. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

-

Domeeni suhtes õigust omaval isikul on võimalus esitada domeeni <%= @domain.name %> registripidajale <%= @registrar.name %> domeeni üleandmise taotlus koos seda tõendava dokumendiga.

+

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

-

Kui üleandmine ei ole <%= @delete_period_length %> päeva jooksul toimunud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> @@ -17,11 +17,13 @@

Dear registrant/administrative contact of .ee domain,

-

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email.

+

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

-

Please, contact your registrar <%= @registrar.name %> with updated contact data, otherwise in <%= @delete_period_length %> days the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction portal. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results.

+

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

Should you have additional questions, please contact your registrar:

<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> @@ -31,15 +33,15 @@

Уважаемый регистрант/административный контакт домена .ee

-

Целевому учреждению Eesti Internet (EIS) стало известно, что юридическое лицо с регистрационным кодом <%= @registrant.reg_no %> удалено из коммерческого реестра.

+

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.

-

Поскольку удаленное из коммерческого регистра юридическое лицо не может являться регистрантом домена, начат <%= @delete_period_length %>-дневный процесс удаления домена <%= @domain.name %>. Домен доступен в интернете на протяжении <%= @expire_warning_period %> дней после начала процесса удаления.

+

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

-

Лицо, обладающее правом на домен, может подать регистратору <%= @registrar.name %> домена <%= @domain.name %> ходатайство о передаче домена, представив вместе с ходатайством подтверждающие документы. Документы должны быть представлены регистратору в течение <%= @delete_period_length %> дней.

+

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

-

Если передача не состоится в течение <%= @delete_period_length %> дней, <%= @domain.force_delete_date %> домен <%= @domain.name %> отправится на доменный аукцион в аукционной среде .ee. Если в течение 24 часов в отношении домена <%= @domain.name %> не поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». Читайте о других возможных результатах аукциона.

+

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: - <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb new file mode 100644 index 000000000..8d2fc58ce --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb @@ -0,0 +1,47 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>

+ +

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

+ +

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.

+ +

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

+ +

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

+ +

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: + <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> From 0116531786704f9ccf6f4402bf1c108a4d05e072 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 12:45:59 +0500 Subject: [PATCH 089/172] Fix existing tests --- app/controllers/admin/domains_controller.rb | 5 ----- app/mailers/domain_delete_mailer.rb | 4 ---- app/models/concerns/domain/force_delete.rb | 8 +++++++- test/mailers/domain_delete_mailer_test.rb | 7 +------ 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb index 2b94607c5..4a8e5961e 100644 --- a/app/controllers/admin/domains_controller.rb +++ b/app/controllers/admin/domains_controller.rb @@ -2,7 +2,6 @@ module Admin class DomainsController < BaseController before_action :set_domain, only: %i[show edit update keep] authorize_resource - helper_method :force_delete_templates def index params[:q] ||= {} @@ -105,9 +104,5 @@ module Admin params[:q][:valid_to_lteq] = ca_cache end - - def force_delete_templates - DomainDeleteMailer.force_delete_templates - end end end diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index 1f08204bf..dbacab68d 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -1,8 +1,4 @@ class DomainDeleteMailer < ApplicationMailer - def self.force_delete_templates - %w[private_person legal_person] - end - def confirmation_request(domain:, registrar:, registrant:) @domain = DomainPresenter.new(domain: domain, view: view_context) @registrar = RegistrarPresenter.new(registrar: registrar, view: view_context) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 78248db80..729a7515b 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -20,7 +20,13 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def notification_template - registrant.org? ? 'legal_person' : 'private_person' + if contact_emails_verification_failed.present? + 'invalid_email' + elsif registrant.org? + 'legal_person' + else + 'private_person' + end end def force_delete_scheduled? diff --git a/test/mailers/domain_delete_mailer_test.rb b/test/mailers/domain_delete_mailer_test.rb index b65ba5d2e..8e568846c 100644 --- a/test/mailers/domain_delete_mailer_test.rb +++ b/test/mailers/domain_delete_mailer_test.rb @@ -5,10 +5,6 @@ class DomainDeleteMailerTest < ActionMailer::TestCase @domain = domains(:shop) end - def test_force_delete_templates - assert_equal %w[private_person legal_person], DomainDeleteMailer.force_delete_templates - end - def test_delivers_confirmation_request_email assert_equal 'shop.test', @domain.name assert_equal 'john@inbox.test', @domain.registrant.email @@ -68,8 +64,7 @@ class DomainDeleteMailerTest < ActionMailer::TestCase email = DomainDeleteMailer.forced(domain: @domain, registrar: @domain.registrar, registrant: @domain.registrant, - template_name: DomainDeleteMailer.force_delete_templates - .first).deliver_now + template_name: @domain.notification_template).deliver_now assert_emails 1 assert_equal ['legal@registry.test'], email.from From 6a4bb6079d03dee21020102a157c7cf3aedcb946 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 13:32:45 +0500 Subject: [PATCH 090/172] Add tests --- app/models/concerns/domain/force_delete.rb | 2 +- app/models/concerns/email_verifable.rb | 2 +- .../previews/domain_delete_mailer_preview.rb | 4 +-- .../admin_area/domains/force_delete_test.rb | 26 +++++++++++++------ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 729a7515b..670e5db67 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -22,7 +22,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength def notification_template if contact_emails_verification_failed.present? 'invalid_email' - elsif registrant.org? + elsif registrant.org? 'legal_person' else 'private_person' diff --git a/app/models/concerns/email_verifable.rb b/app/models/concerns/email_verifable.rb index 2168a0754..d0bb6aecb 100644 --- a/app/models/concerns/email_verifable.rb +++ b/app/models/concerns/email_verifable.rb @@ -16,7 +16,7 @@ module Concerns end def email_verification_failed? - email_verification.failed? + email_verification&.failed? end class_methods do diff --git a/test/mailers/previews/domain_delete_mailer_preview.rb b/test/mailers/previews/domain_delete_mailer_preview.rb index 916b0af0e..9dd7ae7b2 100644 --- a/test/mailers/previews/domain_delete_mailer_preview.rb +++ b/test/mailers/previews/domain_delete_mailer_preview.rb @@ -1,6 +1,6 @@ class DomainDeleteMailerPreview < ActionMailer::Preview def self.define_forced_templates - DomainDeleteMailer.force_delete_templates.each do |template_name| + %w[private_person legal_person invalid_email].each do |template_name| define_method "forced_#{template_name}".to_sym do DomainDeleteMailer.forced(domain: @domain, registrar: @domain.registrar, @@ -34,4 +34,4 @@ class DomainDeleteMailerPreview < ActionMailer::Preview def expired DomainDeleteMailer.expired(@domain) end -end \ No newline at end of file +end diff --git a/test/system/admin_area/domains/force_delete_test.rb b/test/system/admin_area/domains/force_delete_test.rb index 6aa53be6c..e17695fcc 100644 --- a/test/system/admin_area/domains/force_delete_test.rb +++ b/test/system/admin_area/domains/force_delete_test.rb @@ -44,7 +44,7 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase end @domain.reload - assert_equal template_name, @domain.template_name + assert_equal @domain.notification_template, @domain.template_name end def test_uses_legal_template_if_registrant_org @@ -57,7 +57,23 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase end @domain.reload - assert_equal template_name, @domain.template_name + assert_equal @domain.notification_template, @domain.template_name + end + + def test_uses_legal_template_if_invalid_email + verification = @domain.contacts.first.email_verification + verification.update(verified_at: Time.zone.now - 1.day, success: false) + + assert_equal @domain.notification_template, 'invalid_email' + + assert_emails 0 do + visit edit_admin_domain_url(@domain) + find(:css, '#soft_delete').set(true) + click_link_or_button 'Force delete domain' + end + + @domain.reload + assert_equal @domain.notification_template, @domain.template_name end def test_allows_to_skip_notifying_registrant_and_admin_contacts_by_email @@ -87,10 +103,4 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase assert_no_button 'Schedule force delete' assert_no_link 'Schedule force delete' end - - private - - def template_name - @domain.registrant.org? ? 'legal_person' : 'private_person' - end end From e8c2f33e2d6ca13be7fe91dff6ea313ca949a906 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 5 Nov 2020 14:12:10 +0500 Subject: [PATCH 091/172] Add presenter method & preview --- app/presenters/domain_presenter.rb | 4 +++ .../forced/invalid_email.html.erb | 2 +- .../previews/domain_delete_mailer_preview.rb | 28 +++++++++---------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/presenters/domain_presenter.rb b/app/presenters/domain_presenter.rb index 0915722b1..bcbd5d600 100644 --- a/app/presenters/domain_presenter.rb +++ b/app/presenters/domain_presenter.rb @@ -52,6 +52,10 @@ class DomainPresenter end end + def contact_emails_verification_failed + domain.contact_emails_verification_failed.join(', ') + end + def remove_registry_lock_btn return unless domain.locked_by_registrant? diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb index 9e69519e4..817538afd 100644 --- a/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_email.html.erb @@ -1,6 +1,6 @@

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

-

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>

+

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>.

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

diff --git a/test/mailers/previews/domain_delete_mailer_preview.rb b/test/mailers/previews/domain_delete_mailer_preview.rb index 9dd7ae7b2..fa0d9c6b7 100644 --- a/test/mailers/previews/domain_delete_mailer_preview.rb +++ b/test/mailers/previews/domain_delete_mailer_preview.rb @@ -2,9 +2,10 @@ class DomainDeleteMailerPreview < ActionMailer::Preview def self.define_forced_templates %w[private_person legal_person invalid_email].each do |template_name| define_method "forced_#{template_name}".to_sym do - DomainDeleteMailer.forced(domain: @domain, - registrar: @domain.registrar, - registrant: @domain.registrant, + domain = Domain.first + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, template_name: template_name) end end @@ -12,26 +13,25 @@ class DomainDeleteMailerPreview < ActionMailer::Preview define_forced_templates - def initialize - @domain = Domain.first - super - end - def confirmation_request - DomainDeleteMailer.confirmation_request(domain: @domain, - registrar: @domain.registrar, - registrant: @domain.registrant) + domain = Domain.first + DomainDeleteMailer.confirmation_request(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant) end def accepted - DomainDeleteMailer.accepted(@domain) + domain = Domain.first + DomainDeleteMailer.accepted(domain) end def rejected - DomainDeleteMailer.rejected(@domain) + domain = Domain.first + DomainDeleteMailer.rejected(domain) end def expired - DomainDeleteMailer.expired(@domain) + domain = Domain.first + DomainDeleteMailer.expired(domain) end end From 28bede030cd5c255f01789355304b1c439e61d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 5 Nov 2020 12:13:18 +0200 Subject: [PATCH 092/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6721791d2..f7156ef51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +05.11.2020 +* New email template for expired domains in forceDelete [#1725](https://github.com/internetee/registry/issues/1725) + 04.11.2020 * Email notification templates for forceDelete are now automatically selected according to registrant type [#442](https://github.com/internetee/registry/issues/442) From 9a491924f8a0a5f19a7c87726c8552446651ad83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 5 Nov 2020 12:51:51 +0200 Subject: [PATCH 093/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7156ef51..ee8bea7bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 05.11.2020 * New email template for expired domains in forceDelete [#1725](https://github.com/internetee/registry/issues/1725) +* Cancelling forceDelete (FD) restores the state of the domain prior application of FD [#1136](https://github.com/internetee/registry/issues/1136) 04.11.2020 * Email notification templates for forceDelete are now automatically selected according to registrant type [#442](https://github.com/internetee/registry/issues/442) From ed7181e0608aeafc600361b343f896c4e101c946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 5 Nov 2020 14:54:54 +0200 Subject: [PATCH 094/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee8bea7bf..3dd2483f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ 05.11.2020 +* Registrant API contact name update feature [#1724](https://github.com/internetee/registry/issues/1724) * New email template for expired domains in forceDelete [#1725](https://github.com/internetee/registry/issues/1725) * Cancelling forceDelete (FD) restores the state of the domain prior application of FD [#1136](https://github.com/internetee/registry/issues/1136) From 451e1f79afc8f2a5c7ffcecc34e71c6b525575ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 6 Nov 2020 15:51:03 +0200 Subject: [PATCH 095/172] nameserver replacement: scope only to selected domains --- app/api/repp/nameservers_v1.rb | 5 ++++- app/controllers/registrar/nameservers_controller.rb | 12 ++++++++++++ app/models/registrar.rb | 4 +++- .../registrar/bulk_change/_nameserver_form.html.erb | 12 +++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/app/api/repp/nameservers_v1.rb b/app/api/repp/nameservers_v1.rb index 04d7d4f6a..493665b54 100644 --- a/app/api/repp/nameservers_v1.rb +++ b/app/api/repp/nameservers_v1.rb @@ -8,6 +8,7 @@ module Repp requires :data, type: Hash, allow_blank: false do requires :type, type: String, allow_blank: false requires :id, type: String, allow_blank: false + optional :domains, type: Array requires :attributes, type: Hash, allow_blank: false do requires :hostname, type: String, allow_blank: false requires :ipv4, type: Array @@ -28,8 +29,10 @@ module Repp ipv6: params[:data][:attributes][:ipv6], } + domains = params[:data][:domains] || [] + begin - affected_domains = current_user.registrar.replace_nameservers(hostname, new_attributes) + affected_domains = current_user.registrar.replace_nameservers(hostname, new_attributes, domains) rescue ActiveRecord::RecordInvalid => e error!({ errors: e.record.errors.full_messages.map { |error| { title: error } } }, 400) end diff --git a/app/controllers/registrar/nameservers_controller.rb b/app/controllers/registrar/nameservers_controller.rb index 95da7e329..a266aaa2b 100644 --- a/app/controllers/registrar/nameservers_controller.rb +++ b/app/controllers/registrar/nameservers_controller.rb @@ -6,9 +6,13 @@ class Registrar ipv4 = params[:ipv4].split("\r\n") ipv6 = params[:ipv6].split("\r\n") + domains = domain_list_from_csv + + puts "Domains are #{domains}" uri = URI.parse("#{ENV['repp_url']}registrar/nameservers") request = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json') request.body = { data: { type: 'nameserver', id: params[:old_hostname], + domains: domains, attributes: { hostname: params[:new_hostname], ipv4: ipv4, ipv6: ipv6 } } }.to_json @@ -55,5 +59,13 @@ class Registrar render file: 'registrar/bulk_change/new', locals: { active_tab: :nameserver } end end + + def domain_list_from_csv + return [] if params[:batch_file].blank? + + domains = [] + CSV.read(params[:batch_file].path, headers: true).each { |b| domains << b['domain_name'] } + domains + end end end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index e2ffcbfd4..e1fb693ff 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -141,11 +141,13 @@ class Registrar < ApplicationRecord end # Audit log is needed, therefore no raw SQL - def replace_nameservers(hostname, new_attributes) + def replace_nameservers(hostname, new_attributes, domains) transaction do domain_list = [] nameservers.where(hostname: hostname).find_each do |original_nameserver| + next unless domains.include?(original_nameserver.domain.name_puny) || domains.empty? + new_nameserver = Nameserver.new new_nameserver.domain = original_nameserver.domain new_nameserver.attributes = new_attributes diff --git a/app/views/registrar/bulk_change/_nameserver_form.html.erb b/app/views/registrar/bulk_change/_nameserver_form.html.erb index e3f4a0214..491786df7 100644 --- a/app/views/registrar/bulk_change/_nameserver_form.html.erb +++ b/app/views/registrar/bulk_change/_nameserver_form.html.erb @@ -1,4 +1,4 @@ -<%= form_tag registrar_nameservers_path, method: :patch, class: 'form-horizontal' do %> +<%= form_tag registrar_nameservers_path, multipart: true, method: :patch, class: 'form-horizontal' do %> <%= render 'registrar/domain_transfers/form/api_errors' %>
@@ -44,6 +44,16 @@
+
+
+ <%= label_tag 'List of domains' %> +
+
+ <%= file_field_tag :batch_file, required: false, accept: 'text/csv' %> + CSV format, must have domain_name field. List of domains that nameserver change should be scoped to. +
+
+
- <%= file_field_tag :batch_file, required: false, accept: 'text/csv' %> + <%= file_field_tag :puny_file, required: false, accept: 'text/csv' %> CSV format, must have domain_name field. List of domains that nameserver change should be scoped to.
diff --git a/test/system/registrar_area/bulk_change/nameserver_test.rb b/test/system/registrar_area/bulk_change/nameserver_test.rb index d6b3170d5..3d4b4dc70 100644 --- a/test/system/registrar_area/bulk_change/nameserver_test.rb +++ b/test/system/registrar_area/bulk_change/nameserver_test.rb @@ -8,6 +8,7 @@ class RegistrarAreaNameserverBulkChangeTest < ApplicationSystemTestCase def test_replaces_current_registrar_nameservers request_body = { data: { type: 'nameserver', id: 'ns1.bestnames.test', + domains: [], attributes: { hostname: 'new-ns.bestnames.test', ipv4: %w[192.0.2.55 192.0.2.56], ipv6: %w[2001:db8::55 2001:db8::56] } } } From 0570e4352f2cc5095f349c2f0f1308aa46c0f7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 6 Nov 2020 17:51:18 +0200 Subject: [PATCH 098/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72be40d5a..21f710645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ 06.11.2020 +* Csv option to limit list of domains for bulk nameserver change in registrar portal [#1737](https://github.com/internetee/registry/issues/1737) * New forceDelete email template for invalid contact data [#1178](https://github.com/internetee/registry/issues/1178) 05.11.2020 From 4b980a07f52a7f785a74facdf97211d07657dab9 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 9 Nov 2020 17:26:07 +0500 Subject: [PATCH 099/172] Add basic interactor/organizer support --- Gemfile | 2 ++ Gemfile.lock | 6 ++++ app/interactors/domain/force_delete/base.rb | 16 +++++++++ .../domain/force_delete/check_discarded.rb | 14 ++++++++ .../domain/force_delete/post_set_process.rb | 21 +++++++++++ .../domain/force_delete/prepare_domain.rb | 14 ++++++++ .../domain/force_delete/set_status.rb | 36 +++++++++++++++++++ app/models/concerns/domain/force_delete.rb | 4 +++ 8 files changed, 113 insertions(+) create mode 100644 app/interactors/domain/force_delete/base.rb create mode 100644 app/interactors/domain/force_delete/check_discarded.rb create mode 100644 app/interactors/domain/force_delete/post_set_process.rb create mode 100644 app/interactors/domain/force_delete/prepare_domain.rb create mode 100644 app/interactors/domain/force_delete/set_status.rb diff --git a/Gemfile b/Gemfile index 25c3eafff..c4f5a7cf8 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,8 @@ source 'https://rubygems.org' # core gem 'bootsnap', '>= 1.1.0', require: false +gem 'interactor', '~> 3.0' +gem 'interactor-rails', '~> 2.0' gem 'iso8601', '0.12.1' # for dates and times gem 'rails', '~> 6.0' gem 'rest-client' diff --git a/Gemfile.lock b/Gemfile.lock index c628257a2..cc59a8f41 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -251,6 +251,10 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) i18n_data (0.10.0) + interactor (3.1.2) + interactor-rails (2.2.1) + interactor (~> 3.0) + rails (>= 4.2) isikukood (0.1.2) iso8601 (0.12.1) jquery-rails (4.4.0) @@ -544,6 +548,8 @@ DEPENDENCIES figaro (= 1.1.1) grape haml (~> 5.0) + interactor (~> 3.0) + interactor-rails (~> 2.0) isikukood iso8601 (= 0.12.1) jquery-rails diff --git a/app/interactors/domain/force_delete/base.rb b/app/interactors/domain/force_delete/base.rb new file mode 100644 index 000000000..79de3cf2f --- /dev/null +++ b/app/interactors/domain/force_delete/base.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDelete + class Base + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDelete::CheckDiscarded, + Domain::ForceDelete::PrepareDomain, + Domain::ForceDelete::SetStatus, + Domain::ForceDelete::PostSetProcess, + Domain::ForceDelete::NotifyRegistrar, + Domain::ForceDelete::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete/check_discarded.rb new file mode 100644 index 000000000..a13c16778 --- /dev/null +++ b/app/interactors/domain/force_delete/check_discarded.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDelete + class CheckDiscarded + include Interactor + + def call + return unless context.domain.discarded? + + raise StandardError, + 'Force delete procedure cannot be scheduled while a domain is discarded' + end + end + end +end diff --git a/app/interactors/domain/force_delete/post_set_process.rb b/app/interactors/domain/force_delete/post_set_process.rb new file mode 100644 index 000000000..a2fc531b2 --- /dev/null +++ b/app/interactors/domain/force_delete/post_set_process.rb @@ -0,0 +1,21 @@ +class Domain + module ForceDelete + class PostSetProcess + include Interactor + + def call + statuses = context.domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + context.domain.save(validate: false) + end + end + end +end diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete/prepare_domain.rb new file mode 100644 index 000000000..4b3b83064 --- /dev/null +++ b/app/interactors/domain/force_delete/prepare_domain.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDelete + class PrepareDomain + include Interactor + + def call + domain = context.domain + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= domain.class.STATUSES_TO_SET + domain.save(validate: false) + end + end + end +end diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete/set_status.rb new file mode 100644 index 000000000..7ce481623 --- /dev/null +++ b/app/interactors/domain/force_delete/set_status.rb @@ -0,0 +1,36 @@ +class Domain + module ForceDelete + class SetStatus + include Interactor + + def call + domain.force_delete_type = context.type + context.type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + private + + def domain + @domain |= context.domain + end + + def force_delete_fast_track + domain.force_delete_date = force_delete_fast_track_start_date + 1.day + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) + end + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index f3bf96975..c6f9b598f 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -7,6 +7,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength :contact_notification_sent_date, :template_name + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + scope :notification_not_sent, lambda { where("(force_delete_data->>'contact_notification_sent_date') is null") From 09389ea6620150df572dbeedcc493a431bda58e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 9 Nov 2020 16:24:25 +0200 Subject: [PATCH 100/172] Registrant confirms: Poll domain change application --- .../api/v1/registrant/confirms_controller.rb | 55 +++++++++++++++++++ config/routes.rb | 4 +- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/v1/registrant/confirms_controller.rb diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb new file mode 100644 index 000000000..cbc8c5413 --- /dev/null +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -0,0 +1,55 @@ +require 'serializers/registrant_api/domain' + +module Api + module V1 + module Registrant + class ConfirmsController < ::Api::V1::Registrant::BaseController + skip_before_action :authenticate, :set_paper_trail_whodunnit + before_action :set_domain, only: %i[index update] + before_action :verify_updateable, only: %i[index update] + + def index + render json: { + domain_name: @domain.name, + current_registrant: serialized_registrant(@domain.registrant), + new_registrant: serialized_registrant(@domain.pending_registrant) + } + end + + def update + end + + private + + def serialized_registrant(registrant) + { + name: registrant.try(:name), + ident: registrant.try(:ident), + country: registrant.try(:ident_country_code) + } + end + + def confirmation_params + params do |p| + p.require(:name) + p.require(:token) + end + end + + def set_domain + @domain = Domain.find_by(name: confirmation_params[:name]) + return if @domain + + render json: { error: 'Domain not found' }, status: :not_found + end + + def verify_updateable + return if @domain.registrant_update_confirmable?(confirmation_params[:token]) + + render json: { error: 'Application expired or not found' }, + status: :unauthorized + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index f58063fae..0b74a2b97 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,12 +56,12 @@ Rails.application.routes.draw do namespace :v1 do namespace :registrant do post 'auth/eid', to: 'auth#eid' - + get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:token', to: 'confirms#update', constraints: { name: /[^\/]+/ } resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] end resources :contacts, only: %i[index show update], param: :uuid - resources :companies, only: %i[index] end resources :auctions, only: %i[index show update], param: :uuid From 03754b542b917fb6f10209f40a20ee038b610f5b Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 13:50:38 +0500 Subject: [PATCH 101/172] Complete creation of interactors --- .../admin/domains/force_delete_controller.rb | 18 ++++++----- .../domain/force_delete/check_discarded.rb | 4 +-- .../domain/force_delete/notify_by_email.rb | 31 +++++++++++++++++++ .../domain/force_delete/notify_registrar.rb | 16 ++++++++++ .../domain/force_delete/prepare_domain.rb | 6 +++- .../domain/force_delete/set_status.rb | 5 ++- app/models/concerns/domain/force_delete.rb | 15 +++++---- 7 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 app/interactors/domain/force_delete/notify_by_email.rb create mode 100644 app/interactors/domain/force_delete/notify_registrar.rb diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index 6a111926f..ca5588964 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -5,19 +5,21 @@ module Admin authorize! :manage, domain domain.transaction do - domain.schedule_force_delete(type: force_delete_type) - domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - - notify_by_email if notify_by_email? + # domain.schedule_force_delete(type: force_delete_type) + # domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', + # domain_name: domain.name, + # outzone_date: domain.outzone_date, + # purge_date: domain.purge_date)) # added to interactor + # + # notify_by_email if notify_by_email? # added to interactor + Domain::ForceDelete::Base.call(domain: domain, type: force_delete_type) end redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') end def notify_by_email + # added to interactor if force_delete_type == :fast_track send_email domain.update(contact_notification_sent_date: Time.zone.today) @@ -39,10 +41,12 @@ module Admin end def notify_by_email? + # added to interactor ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) end def send_email + # added to interactor DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, registrant: domain.registrant, diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete/check_discarded.rb index a13c16778..ac2cd31b6 100644 --- a/app/interactors/domain/force_delete/check_discarded.rb +++ b/app/interactors/domain/force_delete/check_discarded.rb @@ -6,8 +6,8 @@ class Domain def call return unless context.domain.discarded? - raise StandardError, - 'Force delete procedure cannot be scheduled while a domain is discarded' + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + context.fail!( message: message ) end end end diff --git a/app/interactors/domain/force_delete/notify_by_email.rb b/app/interactors/domain/force_delete/notify_by_email.rb new file mode 100644 index 000000000..949dd5838 --- /dev/null +++ b/app/interactors/domain/force_delete/notify_by_email.rb @@ -0,0 +1,31 @@ +class Domain + module ForceDelete + class NotifyByEmail + include Interactor + + def call + return unless notify_by_email? + + if context.type == :fast_track + send_email + context.domain.update(contact_notification_sent_date: Time.zone.today) + else + context.domain.update(template_name: context.domain.notification_template) + end + end + + private + + def notify_by_email? + ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) + end + + def send_email + DomainDeleteMailer.forced(domain: context.domain, + registrar: context.domain.registrar, + registrant: context.domain.registrant, + template_name: context.domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactors/domain/force_delete/notify_registrar.rb b/app/interactors/domain/force_delete/notify_registrar.rb new file mode 100644 index 000000000..345f4d7eb --- /dev/null +++ b/app/interactors/domain/force_delete/notify_registrar.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDelete + class NotifyRegistrar + include Interactor + + def call + domain = context.domain + + domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete/prepare_domain.rb index 4b3b83064..3bc764dc8 100644 --- a/app/interactors/domain/force_delete/prepare_domain.rb +++ b/app/interactors/domain/force_delete/prepare_domain.rb @@ -3,10 +3,14 @@ class Domain class PrepareDomain include Interactor + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + def call domain = context.domain domain.statuses_before_force_delete = domain.statuses - domain.statuses |= domain.class.STATUSES_TO_SET + domain.statuses |= STATUSES_TO_SET domain.save(validate: false) end end diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete/set_status.rb index 7ce481623..2a6c49daa 100644 --- a/app/interactors/domain/force_delete/set_status.rb +++ b/app/interactors/domain/force_delete/set_status.rb @@ -16,7 +16,10 @@ class Domain end def force_delete_fast_track - domain.force_delete_date = force_delete_fast_track_start_date + 1.day + domain.force_delete_date = Time.zone.today + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + + 1.day domain.force_delete_start = Time.zone.today + 1.day end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index c6f9b598f..768b1d11b 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -7,10 +7,6 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength :contact_notification_sent_date, :template_name - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - scope :notification_not_sent, lambda { where("(force_delete_data->>'contact_notification_sent_date') is null") @@ -57,6 +53,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track) + # added to interactor if discarded? raise StandardError, 'Force delete procedure cannot be scheduled while a domain is discarded' end @@ -72,8 +69,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength preserve_current_statuses_for_force_delete add_force_delete_statuses add_force_delete_type(:fast) - self.force_delete_date = force_delete_fast_track_start_date + 1.day - self.force_delete_start = Time.zone.today + 1.day + self.force_delete_date = force_delete_fast_track_start_date + 1.day # added to interactor + self.force_delete_start = Time.zone.today + 1.day # added to interactor stop_all_pending_actions allow_deletion save(validate: false) @@ -120,12 +117,14 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def soft_delete_dates(years) + # added to interactor self.force_delete_start = valid_to - years.years self.force_delete_date = force_delete_start + Setting.expire_warning_period.days + Setting.redemption_grace_period.days end def stop_all_pending_actions + # added to interactor statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_TRANSFER) statuses.delete(DomainStatus::PENDING_RENEW) @@ -133,6 +132,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def preserve_current_statuses_for_force_delete + # added to interactor update(statuses_before_force_delete: statuses) end @@ -142,6 +142,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def add_force_delete_statuses + # added to interactor self.statuses |= [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED] @@ -155,11 +156,13 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def allow_deletion + # added to interactor statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) end def force_delete_fast_track_start_date + # added to interactor Time.zone.today + Setting.expire_warning_period.days + Setting.redemption_grace_period.days end end From f97dff60029b953a946ba05074416abfc1a1a721 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 14:06:37 +0500 Subject: [PATCH 102/172] Fix tests & interactor process --- .../admin/domains/force_delete_controller.rb | 28 +------ app/interactors/domain/force_delete/base.rb | 16 ---- .../domain/force_delete/notify_by_email.rb | 31 -------- .../domain/force_delete/notify_registrar.rb | 16 ---- .../domain/force_delete_interactor/base.rb | 16 ++++ .../check_discarded.rb | 4 +- .../notify_by_email.rb | 31 ++++++++ .../notify_registrar.rb | 16 ++++ .../post_set_process.rb | 2 +- .../prepare_domain.rb | 2 +- .../set_status.rb | 25 +++--- app/models/concerns/domain/force_delete.rb | 79 +------------------ test/models/domain/force_delete_test.rb | 7 +- test/models/domain_test.rb | 2 +- 14 files changed, 93 insertions(+), 182 deletions(-) delete mode 100644 app/interactors/domain/force_delete/base.rb delete mode 100644 app/interactors/domain/force_delete/notify_by_email.rb delete mode 100644 app/interactors/domain/force_delete/notify_registrar.rb create mode 100644 app/interactors/domain/force_delete_interactor/base.rb rename app/interactors/domain/{force_delete => force_delete_interactor}/check_discarded.rb (77%) create mode 100644 app/interactors/domain/force_delete_interactor/notify_by_email.rb create mode 100644 app/interactors/domain/force_delete_interactor/notify_registrar.rb rename app/interactors/domain/{force_delete => force_delete_interactor}/post_set_process.rb (95%) rename app/interactors/domain/{force_delete => force_delete_interactor}/prepare_domain.rb (93%) rename app/interactors/domain/{force_delete => force_delete_interactor}/set_status.rb (61%) diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index ca5588964..9f660ed71 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -5,29 +5,12 @@ module Admin authorize! :manage, domain domain.transaction do - # domain.schedule_force_delete(type: force_delete_type) - # domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - # domain_name: domain.name, - # outzone_date: domain.outzone_date, - # purge_date: domain.purge_date)) # added to interactor - # - # notify_by_email if notify_by_email? # added to interactor - Domain::ForceDelete::Base.call(domain: domain, type: force_delete_type) + domain.schedule_force_delete(type: force_delete_type, notify_by_email: notify_by_email?) end redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') end - def notify_by_email - # added to interactor - if force_delete_type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - def destroy authorize! :manage, domain domain.cancel_force_delete @@ -41,18 +24,9 @@ module Admin end def notify_by_email? - # added to interactor ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) end - def send_email - # added to interactor - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - def force_delete_type soft_delete? ? :soft : :fast_track end diff --git a/app/interactors/domain/force_delete/base.rb b/app/interactors/domain/force_delete/base.rb deleted file mode 100644 index 79de3cf2f..000000000 --- a/app/interactors/domain/force_delete/base.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDelete - class Base - include Interactor::Organizer - - # As per https://github.com/collectiveidea/interactor#organizers - - organize Domain::ForceDelete::CheckDiscarded, - Domain::ForceDelete::PrepareDomain, - Domain::ForceDelete::SetStatus, - Domain::ForceDelete::PostSetProcess, - Domain::ForceDelete::NotifyRegistrar, - Domain::ForceDelete::NotifyByEmail - end - end -end diff --git a/app/interactors/domain/force_delete/notify_by_email.rb b/app/interactors/domain/force_delete/notify_by_email.rb deleted file mode 100644 index 949dd5838..000000000 --- a/app/interactors/domain/force_delete/notify_by_email.rb +++ /dev/null @@ -1,31 +0,0 @@ -class Domain - module ForceDelete - class NotifyByEmail - include Interactor - - def call - return unless notify_by_email? - - if context.type == :fast_track - send_email - context.domain.update(contact_notification_sent_date: Time.zone.today) - else - context.domain.update(template_name: context.domain.notification_template) - end - end - - private - - def notify_by_email? - ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) - end - - def send_email - DomainDeleteMailer.forced(domain: context.domain, - registrar: context.domain.registrar, - registrant: context.domain.registrant, - template_name: context.domain.notification_template).deliver_now - end - end - end -end diff --git a/app/interactors/domain/force_delete/notify_registrar.rb b/app/interactors/domain/force_delete/notify_registrar.rb deleted file mode 100644 index 345f4d7eb..000000000 --- a/app/interactors/domain/force_delete/notify_registrar.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDelete - class NotifyRegistrar - include Interactor - - def call - domain = context.domain - - domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end - end -end diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb new file mode 100644 index 000000000..d10edcaef --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/base.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class Base + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDeleteInteractor::CheckDiscarded, + Domain::ForceDeleteInteractor::PrepareDomain, + Domain::ForceDeleteInteractor::SetStatus, + Domain::ForceDeleteInteractor::PostSetProcess, + Domain::ForceDeleteInteractor::NotifyRegistrar, + Domain::ForceDeleteInteractor::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete_interactor/check_discarded.rb similarity index 77% rename from app/interactors/domain/force_delete/check_discarded.rb rename to app/interactors/domain/force_delete_interactor/check_discarded.rb index ac2cd31b6..c340f9f43 100644 --- a/app/interactors/domain/force_delete/check_discarded.rb +++ b/app/interactors/domain/force_delete_interactor/check_discarded.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class CheckDiscarded include Interactor @@ -7,7 +7,7 @@ class Domain return unless context.domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' - context.fail!( message: message ) + context.fail!(message: message) end end end diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactors/domain/force_delete_interactor/notify_by_email.rb new file mode 100644 index 000000000..91855b839 --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/notify_by_email.rb @@ -0,0 +1,31 @@ +class Domain + module ForceDeleteInteractor + class NotifyByEmail + include Interactor + + def call + return unless context.notify_by_email + + if context.type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: context.domain.notification_template) + end + end + + private + + def domain + @domain ||= context.domain + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactors/domain/force_delete_interactor/notify_registrar.rb new file mode 100644 index 000000000..bfe35d29a --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/notify_registrar.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class NotifyRegistrar + include Interactor + + def call + domain = context.domain + + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactors/domain/force_delete/post_set_process.rb b/app/interactors/domain/force_delete_interactor/post_set_process.rb similarity index 95% rename from app/interactors/domain/force_delete/post_set_process.rb rename to app/interactors/domain/force_delete_interactor/post_set_process.rb index a2fc531b2..78a039d43 100644 --- a/app/interactors/domain/force_delete/post_set_process.rb +++ b/app/interactors/domain/force_delete_interactor/post_set_process.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class PostSetProcess include Interactor diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete_interactor/prepare_domain.rb similarity index 93% rename from app/interactors/domain/force_delete/prepare_domain.rb rename to app/interactors/domain/force_delete_interactor/prepare_domain.rb index 3bc764dc8..c214a6b88 100644 --- a/app/interactors/domain/force_delete/prepare_domain.rb +++ b/app/interactors/domain/force_delete_interactor/prepare_domain.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class PrepareDomain include Interactor diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete_interactor/set_status.rb similarity index 61% rename from app/interactors/domain/force_delete/set_status.rb rename to app/interactors/domain/force_delete_interactor/set_status.rb index 2a6c49daa..a15b67ce2 100644 --- a/app/interactors/domain/force_delete/set_status.rb +++ b/app/interactors/domain/force_delete_interactor/set_status.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class SetStatus include Interactor @@ -9,23 +9,20 @@ class Domain domain.save(validate: false) end - private - def domain - @domain |= context.domain + @domain ||= context.domain end def force_delete_fast_track domain.force_delete_date = Time.zone.today + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days + - 1.day + expire_warning_period_days + + redemption_grace_period_days domain.force_delete_start = Time.zone.today + 1.day end def force_delete_soft - years = (valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? end def soft_forcedelete_dates(years) @@ -34,6 +31,16 @@ class Domain Setting.expire_warning_period.days + Setting.redemption_grace_period.days end + + private + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end end end end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 768b1d11b..26dc62413 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -52,38 +52,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength force_delete_start + Setting.expire_warning_period.days <= valid_to end - def schedule_force_delete(type: :fast_track) - # added to interactor - if discarded? - raise StandardError, 'Force delete procedure cannot be scheduled while a domain is discarded' - end - - type == :fast_track ? force_delete_fast_track : force_delete_soft - end - - def add_force_delete_type(force_delete_type) - self.force_delete_type = force_delete_type - end - - def force_delete_fast_track - preserve_current_statuses_for_force_delete - add_force_delete_statuses - add_force_delete_type(:fast) - self.force_delete_date = force_delete_fast_track_start_date + 1.day # added to interactor - self.force_delete_start = Time.zone.today + 1.day # added to interactor - stop_all_pending_actions - allow_deletion - save(validate: false) - end - - def force_delete_soft - preserve_current_statuses_for_force_delete - add_force_delete_statuses - add_force_delete_type(:soft) - calculate_soft_delete_date - stop_all_pending_actions - allow_deletion - save(validate: false) + def schedule_force_delete(type: :fast_track, notify_by_email: false) + Domain::ForceDeleteInteractor::Base.call(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data @@ -111,58 +83,15 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength private - def calculate_soft_delete_date - years = (valid_to.to_date - Time.zone.today).to_i / 365 - soft_delete_dates(years) if years.positive? - end - - def soft_delete_dates(years) - # added to interactor - self.force_delete_start = valid_to - years.years - self.force_delete_date = force_delete_start + Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def stop_all_pending_actions - # added to interactor - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - end - - def preserve_current_statuses_for_force_delete - # added to interactor - update(statuses_before_force_delete: statuses) - end - def restore_statuses_before_force_delete self.statuses = statuses_before_force_delete self.statuses_before_force_delete = nil end - def add_force_delete_statuses - # added to interactor - self.statuses |= [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED] - end - def remove_force_delete_statuses statuses.delete(DomainStatus::FORCE_DELETE) statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) statuses.delete(DomainStatus::CLIENT_HOLD) end - - def allow_deletion - # added to interactor - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - end - - def force_delete_fast_track_start_date - # added to interactor - Time.zone.today + Setting.expire_warning_period.days + Setting.redemption_grace_period.days - end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index f0723c326..f5c7e7b06 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,9 +111,10 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - assert_raises StandardError do - @domain.schedule_force_delete(type: :fast_track) - end + context = Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + + assert_not context.success? + assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message end def test_cancels_force_delete diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 4a9240f57..bb4dd37cf 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - @domain.schedule_force_delete(type: :fast_track) + Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 3c7fa8846357c797d51fab1b2cfb6bd099877cdb Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 15:06:32 +0500 Subject: [PATCH 103/172] Inherit all the interactors from base one --- .../domain/force_delete_interactor/base.rb | 13 +++++-------- .../force_delete_interactor/check_discarded.rb | 6 ++---- .../force_delete_interactor/notify_by_email.rb | 10 +--------- .../force_delete_interactor/notify_registrar.rb | 6 +----- .../force_delete_interactor/post_set_process.rb | 8 +++----- .../force_delete_interactor/prepare_domain.rb | 5 +---- .../force_delete_interactor/set_force_delete.rb | 16 ++++++++++++++++ .../domain/force_delete_interactor/set_status.rb | 8 +------- app/models/concerns/domain/force_delete.rb | 6 +++--- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 11 files changed, 35 insertions(+), 47 deletions(-) create mode 100644 app/interactors/domain/force_delete_interactor/set_force_delete.rb diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb index d10edcaef..6724d53e3 100644 --- a/app/interactors/domain/force_delete_interactor/base.rb +++ b/app/interactors/domain/force_delete_interactor/base.rb @@ -1,16 +1,13 @@ class Domain module ForceDeleteInteractor class Base - include Interactor::Organizer + include Interactor - # As per https://github.com/collectiveidea/interactor#organizers + private - organize Domain::ForceDeleteInteractor::CheckDiscarded, - Domain::ForceDeleteInteractor::PrepareDomain, - Domain::ForceDeleteInteractor::SetStatus, - Domain::ForceDeleteInteractor::PostSetProcess, - Domain::ForceDeleteInteractor::NotifyRegistrar, - Domain::ForceDeleteInteractor::NotifyByEmail + def domain + @domain ||= context.domain + end end end end diff --git a/app/interactors/domain/force_delete_interactor/check_discarded.rb b/app/interactors/domain/force_delete_interactor/check_discarded.rb index c340f9f43..b63828f49 100644 --- a/app/interactors/domain/force_delete_interactor/check_discarded.rb +++ b/app/interactors/domain/force_delete_interactor/check_discarded.rb @@ -1,10 +1,8 @@ class Domain module ForceDeleteInteractor - class CheckDiscarded - include Interactor - + class CheckDiscarded < Base def call - return unless context.domain.discarded? + return unless domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' context.fail!(message: message) diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactors/domain/force_delete_interactor/notify_by_email.rb index 91855b839..5263fdd7a 100644 --- a/app/interactors/domain/force_delete_interactor/notify_by_email.rb +++ b/app/interactors/domain/force_delete_interactor/notify_by_email.rb @@ -1,8 +1,6 @@ class Domain module ForceDeleteInteractor - class NotifyByEmail - include Interactor - + class NotifyByEmail < Base def call return unless context.notify_by_email @@ -14,12 +12,6 @@ class Domain end end - private - - def domain - @domain ||= context.domain - end - def send_email DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactors/domain/force_delete_interactor/notify_registrar.rb index bfe35d29a..bd891d5dc 100644 --- a/app/interactors/domain/force_delete_interactor/notify_registrar.rb +++ b/app/interactors/domain/force_delete_interactor/notify_registrar.rb @@ -1,11 +1,7 @@ class Domain module ForceDeleteInteractor - class NotifyRegistrar - include Interactor - + class NotifyRegistrar < Base def call - domain = context.domain - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', domain_name: domain.name, outzone_date: domain.outzone_date, diff --git a/app/interactors/domain/force_delete_interactor/post_set_process.rb b/app/interactors/domain/force_delete_interactor/post_set_process.rb index 78a039d43..4017eb459 100644 --- a/app/interactors/domain/force_delete_interactor/post_set_process.rb +++ b/app/interactors/domain/force_delete_interactor/post_set_process.rb @@ -1,10 +1,8 @@ class Domain module ForceDeleteInteractor - class PostSetProcess - include Interactor - + class PostSetProcess < Base def call - statuses = context.domain.statuses + statuses = domain.statuses # Stop all pending actions statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_TRANSFER) @@ -14,7 +12,7 @@ class Domain # Allow deletion statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - context.domain.save(validate: false) + domain.save(validate: false) end end end diff --git a/app/interactors/domain/force_delete_interactor/prepare_domain.rb b/app/interactors/domain/force_delete_interactor/prepare_domain.rb index c214a6b88..6317bbaf1 100644 --- a/app/interactors/domain/force_delete_interactor/prepare_domain.rb +++ b/app/interactors/domain/force_delete_interactor/prepare_domain.rb @@ -1,14 +1,11 @@ class Domain module ForceDeleteInteractor - class PrepareDomain - include Interactor - + class PrepareDomain < Base STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze def call - domain = context.domain domain.statuses_before_force_delete = domain.statuses domain.statuses |= STATUSES_TO_SET domain.save(validate: false) diff --git a/app/interactors/domain/force_delete_interactor/set_force_delete.rb b/app/interactors/domain/force_delete_interactor/set_force_delete.rb new file mode 100644 index 000000000..4435b8f8c --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/set_force_delete.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class SetForceDelete + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDeleteInteractor::CheckDiscarded, + Domain::ForceDeleteInteractor::PrepareDomain, + Domain::ForceDeleteInteractor::SetStatus, + Domain::ForceDeleteInteractor::PostSetProcess, + Domain::ForceDeleteInteractor::NotifyRegistrar, + Domain::ForceDeleteInteractor::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/set_status.rb b/app/interactors/domain/force_delete_interactor/set_status.rb index a15b67ce2..418952f51 100644 --- a/app/interactors/domain/force_delete_interactor/set_status.rb +++ b/app/interactors/domain/force_delete_interactor/set_status.rb @@ -1,18 +1,12 @@ class Domain module ForceDeleteInteractor - class SetStatus - include Interactor - + class SetStatus < Base def call domain.force_delete_type = context.type context.type == :fast_track ? force_delete_fast_track : force_delete_soft domain.save(validate: false) end - def domain - @domain ||= context.domain - end - def force_delete_fast_track domain.force_delete_date = Time.zone.today + expire_warning_period_days + diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 26dc62413..4a032ab36 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,9 +53,9 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteractor::Base.call(domain: self, - type: type, - notify_by_email: notify_by_email) + Domain::ForceDeleteInteractor::SetForceDelete.call(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index f5c7e7b06..ddd330f47 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,7 +111,7 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - context = Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + context = Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) assert_not context.success? assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index bb4dd37cf..2b01c795f 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 377a95cc76713b40fd9b2890811bdcb2190a39db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 13:09:32 +0200 Subject: [PATCH 104/172] Add experimental registrant change accept/deny API endpoint --- .../api/v1/registrant/confirms_controller.rb | 13 +++++++++++++ app/services/registrant_change.rb | 1 + 2 files changed, 14 insertions(+) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index cbc8c5413..390753fd3 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -17,10 +17,23 @@ module Api end def update + verification = RegistrantVerification.new(domain_id: @domain.id, + verification_token: confirmation_params[:token]) + + head(update_action(verification) ? :ok : :bad_request) end private + def update_action(verification) + initiator = "email link, #{t(:user_not_authenticated)}" + if params[:confirm].present? + verification.domain_registrant_change_confirm!(initiator) + else + verification.domain_registrant_change_reject!(initiator) + end + end + def serialized_registrant(registrant) { name: registrant.try(:name), diff --git a/app/services/registrant_change.rb b/app/services/registrant_change.rb index 35b631fb6..fdee7654a 100644 --- a/app/services/registrant_change.rb +++ b/app/services/registrant_change.rb @@ -5,6 +5,7 @@ class RegistrantChange end def confirm + Dispute.close_by_domain(@domain.name) if @domain.disputed? notify_registrant end From 55e66724cf2ae6be3394a0074b469d4adc8578e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:21:32 +0200 Subject: [PATCH 105/172] Clean up verifications controller --- .../api/v1/registrant/confirms_controller.rb | 39 ++++++++++++++----- config/routes.rb | 3 +- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 390753fd3..7d3dd5552 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -7,6 +7,7 @@ module Api skip_before_action :authenticate, :set_paper_trail_whodunnit before_action :set_domain, only: %i[index update] before_action :verify_updateable, only: %i[index update] + before_action :verify_decision, only: %i[update] def index render json: { @@ -18,16 +19,30 @@ module Api def update verification = RegistrantVerification.new(domain_id: @domain.id, - verification_token: confirmation_params[:token]) + verification_token: verify_params[:token]) - head(update_action(verification) ? :ok : :bad_request) + head(:bad_request) and return unless update_action(verification) + + render json: { + domain_name: @domain.name, + current_registrant: serialized_registrant(current_registrant), + status: params[:decision] + } end private + def current_registrant + changes_registrant? ? @domain.registrant : @domain.pending_registrant + end + + def changes_registrant? + params[:decision] == 'confirmed' + end + def update_action(verification) - initiator = "email link, #{t(:user_not_authenticated)}" - if params[:confirm].present? + initiator = "email link, #{I18n.t(:user_not_authenticated)}" + if changes_registrant? verification.domain_registrant_change_confirm!(initiator) else verification.domain_registrant_change_reject!(initiator) @@ -42,25 +57,31 @@ module Api } end - def confirmation_params + def verify_params params do |p| p.require(:name) p.require(:token) end end + def verify_decision + return if %w[confirmed rejected].include?(params[:decision]) + + head :bad_request + end + def set_domain - @domain = Domain.find_by(name: confirmation_params[:name]) + @domain = Domain.find_by(name: verify_params[:name]) + @domain ||= Domain.find_by(name_puny: verify_params[:name]) return if @domain render json: { error: 'Domain not found' }, status: :not_found end def verify_updateable - return if @domain.registrant_update_confirmable?(confirmation_params[:token]) + return if @domain.registrant_update_confirmable?(verify_params[:token]) - render json: { error: 'Application expired or not found' }, - status: :unauthorized + render json: { error: 'Application expired or not found' }, status: :unauthorized end end end diff --git a/config/routes.rb b/config/routes.rb index 0b74a2b97..107484f6e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,7 +57,8 @@ Rails.application.routes.draw do namespace :registrant do post 'auth/eid', to: 'auth#eid' get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } - post 'confirms/:name/:token', to: 'confirms#update', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } + resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] end From 6e1a836c97e72a3fd0183835178362766246903d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:23:01 +0200 Subject: [PATCH 106/172] Fix CC issues --- app/controllers/api/v1/registrant/confirms_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 7d3dd5552..712df6bc1 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -13,7 +13,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(@domain.registrant), - new_registrant: serialized_registrant(@domain.pending_registrant) + new_registrant: serialized_registrant(@domain.pending_registrant), } end @@ -26,7 +26,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(current_registrant), - status: params[:decision] + status: params[:decision], } end @@ -53,7 +53,7 @@ module Api { name: registrant.try(:name), ident: registrant.try(:ident), - country: registrant.try(:ident_country_code) + country: registrant.try(:ident_country_code), } end From bce39e3404535535ce2097f2b46069e5872b092e Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 11 Nov 2020 15:44:00 +0500 Subject: [PATCH 107/172] Move interactor from Interactor gem to ActiveInteraction --- Gemfile | 3 +-- Gemfile.lock | 9 +++------ .../admin/domains/force_delete_controller.rb | 8 ++++++-- .../domain/force_delete_interaction/base.rb | 17 +++++++++++++++++ .../check_discarded.rb | 8 ++++---- .../notify_by_email.rb | 10 +++++----- .../notify_registrar.rb | 4 ++-- .../post_set_process.rb | 4 ++-- .../force_delete_interaction}/prepare_domain.rb | 4 ++-- .../set_force_delete.rb | 14 ++++++++++++++ .../force_delete_interaction}/set_status.rb | 12 ++++++------ .../domain/force_delete_interactor/base.rb | 13 ------------- .../force_delete_interactor/set_force_delete.rb | 16 ---------------- app/models/concerns/domain/force_delete.rb | 2 +- config/application.rb | 1 + test/models/domain/force_delete_test.rb | 8 +++++--- test/models/domain_test.rb | 2 +- 17 files changed, 70 insertions(+), 65 deletions(-) create mode 100644 app/interactions/domain/force_delete_interaction/base.rb rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/check_discarded.rb (55%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/notify_by_email.rb (70%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/notify_registrar.rb (89%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/post_set_process.rb (91%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/prepare_domain.rb (89%) create mode 100644 app/interactions/domain/force_delete_interaction/set_force_delete.rb rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/set_status.rb (86%) delete mode 100644 app/interactors/domain/force_delete_interactor/base.rb delete mode 100644 app/interactors/domain/force_delete_interactor/set_force_delete.rb diff --git a/Gemfile b/Gemfile index c4f5a7cf8..d35238fc0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,8 @@ source 'https://rubygems.org' # core +gem 'active_interaction', '~> 3.8' gem 'bootsnap', '>= 1.1.0', require: false -gem 'interactor', '~> 3.0' -gem 'interactor-rails', '~> 2.0' gem 'iso8601', '0.12.1' # for dates and times gem 'rails', '~> 6.0' gem 'rest-client' diff --git a/Gemfile.lock b/Gemfile.lock index cc59a8f41..14970c2c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -112,6 +112,8 @@ GEM 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.3) activesupport (= 6.0.3.3) globalid (>= 0.3.6) @@ -251,10 +253,6 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) i18n_data (0.10.0) - interactor (3.1.2) - interactor-rails (2.2.1) - interactor (~> 3.0) - rails (>= 4.2) isikukood (0.1.2) iso8601 (0.12.1) jquery-rails (4.4.0) @@ -525,6 +523,7 @@ PLATFORMS ruby DEPENDENCIES + active_interaction (~> 3.8) activerecord-import airbrake bootsnap (>= 1.1.0) @@ -548,8 +547,6 @@ DEPENDENCIES figaro (= 1.1.1) grape haml (~> 5.0) - interactor (~> 3.0) - interactor-rails (~> 2.0) isikukood iso8601 (= 0.12.1) jquery-rails diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index 9f660ed71..4fe85fa3b 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -4,11 +4,15 @@ module Admin def create authorize! :manage, domain + notice = t('.scheduled') + domain.transaction do - domain.schedule_force_delete(type: force_delete_type, notify_by_email: notify_by_email?) + result = domain.schedule_force_delete(type: force_delete_type, + notify_by_email: notify_by_email?) + notice = result.errors.messages[:domain].first unless result.valid? end - redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') + redirect_to edit_admin_domain_url(domain), notice: notice end def destroy diff --git a/app/interactions/domain/force_delete_interaction/base.rb b/app/interactions/domain/force_delete_interaction/base.rb new file mode 100644 index 000000000..e8079982b --- /dev/null +++ b/app/interactions/domain/force_delete_interaction/base.rb @@ -0,0 +1,17 @@ +class Domain + module ForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/check_discarded.rb b/app/interactions/domain/force_delete_interaction/check_discarded.rb similarity index 55% rename from app/interactors/domain/force_delete_interactor/check_discarded.rb rename to app/interactions/domain/force_delete_interaction/check_discarded.rb index b63828f49..d2b54641a 100644 --- a/app/interactors/domain/force_delete_interactor/check_discarded.rb +++ b/app/interactions/domain/force_delete_interaction/check_discarded.rb @@ -1,11 +1,11 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class CheckDiscarded < Base - def call - return unless domain.discarded? + def execute + return true unless domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' - context.fail!(message: message) + errors.add(:domain, message) end end end diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactions/domain/force_delete_interaction/notify_by_email.rb similarity index 70% rename from app/interactors/domain/force_delete_interactor/notify_by_email.rb rename to app/interactions/domain/force_delete_interaction/notify_by_email.rb index 5263fdd7a..97abc8e5c 100644 --- a/app/interactors/domain/force_delete_interactor/notify_by_email.rb +++ b/app/interactions/domain/force_delete_interaction/notify_by_email.rb @@ -1,14 +1,14 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class NotifyByEmail < Base - def call - return unless context.notify_by_email + def execute + return unless notify_by_email - if context.type == :fast_track + if type == :fast_track send_email domain.update(contact_notification_sent_date: Time.zone.today) else - domain.update(template_name: context.domain.notification_template) + domain.update(template_name: domain.notification_template) end end diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactions/domain/force_delete_interaction/notify_registrar.rb similarity index 89% rename from app/interactors/domain/force_delete_interactor/notify_registrar.rb rename to app/interactions/domain/force_delete_interaction/notify_registrar.rb index bd891d5dc..da3e400cc 100644 --- a/app/interactors/domain/force_delete_interactor/notify_registrar.rb +++ b/app/interactions/domain/force_delete_interaction/notify_registrar.rb @@ -1,7 +1,7 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class NotifyRegistrar < Base - def call + def execute domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', domain_name: domain.name, outzone_date: domain.outzone_date, diff --git a/app/interactors/domain/force_delete_interactor/post_set_process.rb b/app/interactions/domain/force_delete_interaction/post_set_process.rb similarity index 91% rename from app/interactors/domain/force_delete_interactor/post_set_process.rb rename to app/interactions/domain/force_delete_interaction/post_set_process.rb index 4017eb459..68eb59bf9 100644 --- a/app/interactors/domain/force_delete_interactor/post_set_process.rb +++ b/app/interactions/domain/force_delete_interaction/post_set_process.rb @@ -1,7 +1,7 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class PostSetProcess < Base - def call + def execute statuses = domain.statuses # Stop all pending actions statuses.delete(DomainStatus::PENDING_UPDATE) diff --git a/app/interactors/domain/force_delete_interactor/prepare_domain.rb b/app/interactions/domain/force_delete_interaction/prepare_domain.rb similarity index 89% rename from app/interactors/domain/force_delete_interactor/prepare_domain.rb rename to app/interactions/domain/force_delete_interaction/prepare_domain.rb index 6317bbaf1..fddfeb75a 100644 --- a/app/interactors/domain/force_delete_interactor/prepare_domain.rb +++ b/app/interactions/domain/force_delete_interaction/prepare_domain.rb @@ -1,11 +1,11 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class PrepareDomain < Base STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - def call + def execute domain.statuses_before_force_delete = domain.statuses domain.statuses |= STATUSES_TO_SET domain.save(validate: false) diff --git a/app/interactions/domain/force_delete_interaction/set_force_delete.rb b/app/interactions/domain/force_delete_interaction/set_force_delete.rb new file mode 100644 index 000000000..6bbd8ef20 --- /dev/null +++ b/app/interactions/domain/force_delete_interaction/set_force_delete.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDeleteInteraction + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/set_status.rb b/app/interactions/domain/force_delete_interaction/set_status.rb similarity index 86% rename from app/interactors/domain/force_delete_interactor/set_status.rb rename to app/interactions/domain/force_delete_interaction/set_status.rb index 418952f51..a56069fcc 100644 --- a/app/interactors/domain/force_delete_interactor/set_status.rb +++ b/app/interactions/domain/force_delete_interaction/set_status.rb @@ -1,9 +1,9 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class SetStatus < Base - def call - domain.force_delete_type = context.type - context.type == :fast_track ? force_delete_fast_track : force_delete_soft + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft domain.save(validate: false) end @@ -19,6 +19,8 @@ class Domain soft_forcedelete_dates(years) if years.positive? end + private + def soft_forcedelete_dates(years) domain.force_delete_start = domain.valid_to - years.years domain.force_delete_date = domain.force_delete_start + @@ -26,8 +28,6 @@ class Domain Setting.redemption_grace_period.days end - private - def redemption_grace_period_days Setting.redemption_grace_period.days + 1.day end diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb deleted file mode 100644 index 6724d53e3..000000000 --- a/app/interactors/domain/force_delete_interactor/base.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Domain - module ForceDeleteInteractor - class Base - include Interactor - - private - - def domain - @domain ||= context.domain - end - end - end -end diff --git a/app/interactors/domain/force_delete_interactor/set_force_delete.rb b/app/interactors/domain/force_delete_interactor/set_force_delete.rb deleted file mode 100644 index 4435b8f8c..000000000 --- a/app/interactors/domain/force_delete_interactor/set_force_delete.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDeleteInteractor - class SetForceDelete - include Interactor::Organizer - - # As per https://github.com/collectiveidea/interactor#organizers - - organize Domain::ForceDeleteInteractor::CheckDiscarded, - Domain::ForceDeleteInteractor::PrepareDomain, - Domain::ForceDeleteInteractor::SetStatus, - Domain::ForceDeleteInteractor::PostSetProcess, - Domain::ForceDeleteInteractor::NotifyRegistrar, - Domain::ForceDeleteInteractor::NotifyByEmail - end - end -end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 4a032ab36..f81669c74 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,7 +53,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteractor::SetForceDelete.call(domain: self, + Domain::ForceDeleteInteraction::SetForceDelete.run(domain: self, type: type, notify_by_email: notify_by_email) end diff --git a/config/application.rb b/config/application.rb index 5f4481512..a5fb17c9d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -36,6 +36,7 @@ module DomainNameRegistry # Autoload all model subdirs config.autoload_paths += Dir[Rails.root.join('app', 'models', '**/')] + config.autoload_paths += Dir[Rails.root.join('app', 'interactions', '**/')] config.eager_load_paths << config.root.join('lib', 'validators') config.watchable_dirs['lib'] = %i[rb] diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index ddd330f47..b57763342 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,10 +111,12 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - context = Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) + result = Domain::ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) - assert_not context.success? - assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message + assert_not result.valid? + assert_not @domain.force_delete_scheduled? + message = ["Force delete procedure cannot be scheduled while a domain is discarded"] + assert_equal message, result.errors.messages[:domain] end def test_cancels_force_delete diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 2b01c795f..514efaf14 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) + Domain::ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From e62f0a077abda210a76866e1184d0e420391aa34 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 11 Nov 2020 21:16:27 +0500 Subject: [PATCH 108/172] Remove domain namespace --- .../domain/force_delete_interaction/base.rb | 17 -------- .../check_discarded.rb | 12 ------ .../notify_by_email.rb | 23 ----------- .../notify_registrar.rb | 12 ------ .../post_set_process.rb | 19 --------- .../prepare_domain.rb | 15 ------- .../set_force_delete.rb | 14 ------- .../force_delete_interaction/set_status.rb | 40 ------------------- .../force_delete_interaction/base.rb | 16 ++++++++ .../check_discarded.rb | 11 +++++ .../notify_by_email.rb | 21 ++++++++++ .../notify_registrar.rb | 10 +++++ .../post_set_process.rb | 17 ++++++++ .../prepare_domain.rb | 13 ++++++ .../set_force_delete.rb | 12 ++++++ .../force_delete_interaction/set_status.rb | 38 ++++++++++++++++++ app/models/concerns/domain/force_delete.rb | 6 +-- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 19 files changed, 143 insertions(+), 157 deletions(-) delete mode 100644 app/interactions/domain/force_delete_interaction/base.rb delete mode 100644 app/interactions/domain/force_delete_interaction/check_discarded.rb delete mode 100644 app/interactions/domain/force_delete_interaction/notify_by_email.rb delete mode 100644 app/interactions/domain/force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/domain/force_delete_interaction/post_set_process.rb delete mode 100644 app/interactions/domain/force_delete_interaction/prepare_domain.rb delete mode 100644 app/interactions/domain/force_delete_interaction/set_force_delete.rb delete mode 100644 app/interactions/domain/force_delete_interaction/set_status.rb create mode 100644 app/interactions/force_delete_interaction/base.rb create mode 100644 app/interactions/force_delete_interaction/check_discarded.rb create mode 100644 app/interactions/force_delete_interaction/notify_by_email.rb create mode 100644 app/interactions/force_delete_interaction/notify_registrar.rb create mode 100644 app/interactions/force_delete_interaction/post_set_process.rb create mode 100644 app/interactions/force_delete_interaction/prepare_domain.rb create mode 100644 app/interactions/force_delete_interaction/set_force_delete.rb create mode 100644 app/interactions/force_delete_interaction/set_status.rb diff --git a/app/interactions/domain/force_delete_interaction/base.rb b/app/interactions/domain/force_delete_interaction/base.rb deleted file mode 100644 index e8079982b..000000000 --- a/app/interactions/domain/force_delete_interaction/base.rb +++ /dev/null @@ -1,17 +0,0 @@ -class Domain - module ForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to set ForceDelete on' - symbol :type, - default: :fast_track, - description: 'Force delete type, might be :fast_track or :soft' - boolean :notify_by_email, - default: false, - description: 'Do we need to send email notification' - - validates :type, inclusion: { in: %i[fast_track soft] } - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/check_discarded.rb b/app/interactions/domain/force_delete_interaction/check_discarded.rb deleted file mode 100644 index d2b54641a..000000000 --- a/app/interactions/domain/force_delete_interaction/check_discarded.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Domain - module ForceDeleteInteraction - class CheckDiscarded < Base - def execute - return true unless domain.discarded? - - message = 'Force delete procedure cannot be scheduled while a domain is discarded' - errors.add(:domain, message) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/notify_by_email.rb b/app/interactions/domain/force_delete_interaction/notify_by_email.rb deleted file mode 100644 index 97abc8e5c..000000000 --- a/app/interactions/domain/force_delete_interaction/notify_by_email.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Domain - module ForceDeleteInteraction - class NotifyByEmail < Base - def execute - return unless notify_by_email - - if type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - - def send_email - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/notify_registrar.rb b/app/interactions/domain/force_delete_interaction/notify_registrar.rb deleted file mode 100644 index da3e400cc..000000000 --- a/app/interactions/domain/force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Domain - module ForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/post_set_process.rb b/app/interactions/domain/force_delete_interaction/post_set_process.rb deleted file mode 100644 index 68eb59bf9..000000000 --- a/app/interactions/domain/force_delete_interaction/post_set_process.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Domain - module ForceDeleteInteraction - class PostSetProcess < Base - def execute - statuses = domain.statuses - # Stop all pending actions - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - - # Allow deletion - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - domain.save(validate: false) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/prepare_domain.rb b/app/interactions/domain/force_delete_interaction/prepare_domain.rb deleted file mode 100644 index fddfeb75a..000000000 --- a/app/interactions/domain/force_delete_interaction/prepare_domain.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Domain - module ForceDeleteInteraction - class PrepareDomain < Base - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - - def execute - domain.statuses_before_force_delete = domain.statuses - domain.statuses |= STATUSES_TO_SET - domain.save(validate: false) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/set_force_delete.rb b/app/interactions/domain/force_delete_interaction/set_force_delete.rb deleted file mode 100644 index 6bbd8ef20..000000000 --- a/app/interactions/domain/force_delete_interaction/set_force_delete.rb +++ /dev/null @@ -1,14 +0,0 @@ -class Domain - module ForceDeleteInteraction - class SetForceDelete < Base - def execute - compose(CheckDiscarded, inputs) - compose(PrepareDomain, inputs) - compose(SetStatus, inputs) - compose(PostSetProcess, inputs) - compose(NotifyRegistrar, inputs) - compose(NotifyByEmail, inputs) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/set_status.rb b/app/interactions/domain/force_delete_interaction/set_status.rb deleted file mode 100644 index a56069fcc..000000000 --- a/app/interactions/domain/force_delete_interaction/set_status.rb +++ /dev/null @@ -1,40 +0,0 @@ -class Domain - module ForceDeleteInteraction - class SetStatus < Base - def execute - domain.force_delete_type = type - type == :fast_track ? force_delete_fast_track : force_delete_soft - domain.save(validate: false) - end - - def force_delete_fast_track - domain.force_delete_date = Time.zone.today + - expire_warning_period_days + - redemption_grace_period_days - domain.force_delete_start = Time.zone.today + 1.day - end - - def force_delete_soft - years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) if years.positive? - end - - private - - def soft_forcedelete_dates(years) - domain.force_delete_start = domain.valid_to - years.years - domain.force_delete_date = domain.force_delete_start + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def redemption_grace_period_days - Setting.redemption_grace_period.days + 1.day - end - - def expire_warning_period_days - Setting.expire_warning_period.days - end - end - end -end diff --git a/app/interactions/force_delete_interaction/base.rb b/app/interactions/force_delete_interaction/base.rb new file mode 100644 index 000000000..4764ce8fe --- /dev/null +++ b/app/interactions/force_delete_interaction/base.rb @@ -0,0 +1,16 @@ +module ForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end +end + diff --git a/app/interactions/force_delete_interaction/check_discarded.rb b/app/interactions/force_delete_interaction/check_discarded.rb new file mode 100644 index 000000000..e0c893294 --- /dev/null +++ b/app/interactions/force_delete_interaction/check_discarded.rb @@ -0,0 +1,11 @@ +module ForceDeleteInteraction + class CheckDiscarded < Base + def execute + return true unless domain.discarded? + + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + errors.add(:domain, message) + end + end +end + diff --git a/app/interactions/force_delete_interaction/notify_by_email.rb b/app/interactions/force_delete_interaction/notify_by_email.rb new file mode 100644 index 000000000..541df258d --- /dev/null +++ b/app/interactions/force_delete_interaction/notify_by_email.rb @@ -0,0 +1,21 @@ +module ForceDeleteInteraction + class NotifyByEmail < Base + def execute + return unless notify_by_email + + if type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: domain.notification_template) + end + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end +end diff --git a/app/interactions/force_delete_interaction/notify_registrar.rb b/app/interactions/force_delete_interaction/notify_registrar.rb new file mode 100644 index 000000000..691e54f7e --- /dev/null +++ b/app/interactions/force_delete_interaction/notify_registrar.rb @@ -0,0 +1,10 @@ +module ForceDeleteInteraction + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end +end diff --git a/app/interactions/force_delete_interaction/post_set_process.rb b/app/interactions/force_delete_interaction/post_set_process.rb new file mode 100644 index 000000000..e51acf9b7 --- /dev/null +++ b/app/interactions/force_delete_interaction/post_set_process.rb @@ -0,0 +1,17 @@ +module ForceDeleteInteraction + class PostSetProcess < Base + def execute + statuses = domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + domain.save(validate: false) + end + end +end diff --git a/app/interactions/force_delete_interaction/prepare_domain.rb b/app/interactions/force_delete_interaction/prepare_domain.rb new file mode 100644 index 000000000..97f364145 --- /dev/null +++ b/app/interactions/force_delete_interaction/prepare_domain.rb @@ -0,0 +1,13 @@ +module ForceDeleteInteraction + class PrepareDomain < Base + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + + def execute + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= STATUSES_TO_SET + domain.save(validate: false) + end + end +end diff --git a/app/interactions/force_delete_interaction/set_force_delete.rb b/app/interactions/force_delete_interaction/set_force_delete.rb new file mode 100644 index 000000000..4608b5127 --- /dev/null +++ b/app/interactions/force_delete_interaction/set_force_delete.rb @@ -0,0 +1,12 @@ +module ForceDeleteInteraction + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end +end diff --git a/app/interactions/force_delete_interaction/set_status.rb b/app/interactions/force_delete_interaction/set_status.rb new file mode 100644 index 000000000..7d104aeee --- /dev/null +++ b/app/interactions/force_delete_interaction/set_status.rb @@ -0,0 +1,38 @@ +module ForceDeleteInteraction + class SetStatus < Base + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + def force_delete_fast_track + domain.force_delete_date = Time.zone.today + + expire_warning_period_days + + redemption_grace_period_days + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? + end + + private + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index f81669c74..b119b0ce0 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,9 +53,9 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteraction::SetForceDelete.run(domain: self, - type: type, - notify_by_email: notify_by_email) + ForceDeleteInteraction::SetForceDelete.run(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index b57763342..2c264d916 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,7 +111,7 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - result = Domain::ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) + result = ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) assert_not result.valid? assert_not @domain.force_delete_scheduled? diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 514efaf14..cc88cf35f 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) + ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 4eaa8065ba3efbb5cb6890ef8215ef3c4b055b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:49:26 +0200 Subject: [PATCH 109/172] Mailer: Enable registrant confirm actions via REST --- app/mailers/domain_delete_mailer.rb | 7 ++++++- config/application.yml.sample | 3 +++ config/routes.rb | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index 1f08204bf..c4190fe14 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -53,7 +53,12 @@ class DomainDeleteMailer < ApplicationMailer private def confirmation_url(domain) - registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) + base_url = ENV['registrant_portal_verifications_base_url'] + if base_url.blank? + registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) + else + "#{base_url}/confirmation/#{domain.name_puny}/#{domain.registrant_verification_token}" + end end def forced_email_from diff --git a/config/application.yml.sample b/config/application.yml.sample index ab64ed35e..acaa536dd 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -87,6 +87,9 @@ sk_digi_doc_service_name: 'Testimine' registrant_api_base_url: registrant_api_auth_allowed_ips: '127.0.0.1, 0.0.0.0' #ips, separated with commas +# Base URL (inc. https://) of REST registrant portal +# Leave blank to use internal registrant portal +registrant_portal_verifications_base_url: '' # # MISC diff --git a/config/routes.rb b/config/routes.rb index 107484f6e..7061f125f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,6 +63,7 @@ Rails.application.routes.draw do resource :registry_lock, only: %i[create destroy] end resources :contacts, only: %i[index show update], param: :uuid + resources :companies, only: %i[index] end resources :auctions, only: %i[index show update], param: :uuid From 64d35a864f840ea4db72f5e9af047c1b4a4b3fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 11:19:16 +0200 Subject: [PATCH 110/172] Add delete action to confirmations API endpoint --- .../api/v1/registrant/confirms_controller.rb | 49 +++++++++++++------ app/mailers/domain_delete_mailer.rb | 2 +- app/mailers/registrant_change_mailer.rb | 7 ++- config/routes.rb | 4 +- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 712df6bc1..b04a72449 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -6,7 +6,7 @@ module Api class ConfirmsController < ::Api::V1::Registrant::BaseController skip_before_action :authenticate, :set_paper_trail_whodunnit before_action :set_domain, only: %i[index update] - before_action :verify_updateable, only: %i[index update] + before_action :verify_action, only: %i[index update] before_action :verify_decision, only: %i[update] def index @@ -21,7 +21,10 @@ module Api verification = RegistrantVerification.new(domain_id: @domain.id, verification_token: verify_params[:token]) - head(:bad_request) and return unless update_action(verification) + unless delete_action? ? delete_action(verification) : change_action(verification) + head :bad_request + return + end render json: { domain_name: @domain.name, @@ -32,21 +35,28 @@ module Api private - def current_registrant - changes_registrant? ? @domain.registrant : @domain.pending_registrant + def initiator + "email link, #{I18n.t(:user_not_authenticated)}" end - def changes_registrant? + def current_registrant + approved? ? @domain.registrant : @domain.pending_registrant + end + + def approved? params[:decision] == 'confirmed' end - def update_action(verification) - initiator = "email link, #{I18n.t(:user_not_authenticated)}" - if changes_registrant? - verification.domain_registrant_change_confirm!(initiator) - else - verification.domain_registrant_change_reject!(initiator) - end + def change_action(verification) + return verification.domain_registrant_change_confirm!(initiator) if approved? + + verification.domain_registrant_change_reject!(initiator) + end + + def delete_action(verification) + return verification.domain_registrant_delete_confirm!(initiator) if approved? + + verification.domain_registrant_delete_reject!(initiator) end def serialized_registrant(registrant) @@ -59,11 +69,18 @@ module Api def verify_params params do |p| + p.require(:template) p.require(:name) p.require(:token) end end + def delete_action? + return true if params[:template] == 'delete' + + false + end + def verify_decision return if %w[confirmed rejected].include?(params[:decision]) @@ -78,8 +95,12 @@ module Api render json: { error: 'Domain not found' }, status: :not_found end - def verify_updateable - return if @domain.registrant_update_confirmable?(verify_params[:token]) + def verify_action + if params[:template] == 'change' + return true if @domain.registrant_update_confirmable?(verify_params[:token]) + elsif params[:template] == 'delete' + return true if @domain.registrant_delete_confirmable?(verify_params[:token]) + end render json: { error: 'Application expired or not found' }, status: :unauthorized end diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index c4190fe14..8e2b1a341 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -57,7 +57,7 @@ class DomainDeleteMailer < ApplicationMailer if base_url.blank? registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) else - "#{base_url}/confirmation/#{domain.name_puny}/#{domain.registrant_verification_token}" + "#{base_url}/confirmation/#{domain.name_puny}/delete/#{domain.registrant_verification_token}" end end diff --git a/app/mailers/registrant_change_mailer.rb b/app/mailers/registrant_change_mailer.rb index ff3cfa18e..3e97f4b86 100644 --- a/app/mailers/registrant_change_mailer.rb +++ b/app/mailers/registrant_change_mailer.rb @@ -50,7 +50,12 @@ class RegistrantChangeMailer < ApplicationMailer private def confirmation_url(domain) - registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) + base_url = ENV['registrant_portal_verifications_base_url'] + if base_url.blank? + registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) + else + "#{base_url}/confirmation/#{domain.name_puny}/change/#{domain.registrant_verification_token}" + end end def address_processing diff --git a/config/routes.rb b/config/routes.rb index 7061f125f..440c9c05e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,8 +56,8 @@ Rails.application.routes.draw do namespace :v1 do namespace :registrant do post 'auth/eid', to: 'auth#eid' - get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } - post 'confirms/:name/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } + get 'confirms/:name/:template/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:template/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] From c42c482d64f61ca27af202dce82ef7e1e3a6d9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 13:38:09 +0200 Subject: [PATCH 111/172] Fix CC issues --- .../api/v1/registrant/confirms_controller.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index b04a72449..d03e7ab93 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -26,11 +26,9 @@ module Api return end - render json: { - domain_name: @domain.name, - current_registrant: serialized_registrant(current_registrant), - status: params[:decision], - } + render json: { domain_name: @domain.name, + current_registrant: serialized_registrant(current_registrant), + status: params[:decision] } end private @@ -96,11 +94,13 @@ module Api end def verify_action - if params[:template] == 'change' - return true if @domain.registrant_update_confirmable?(verify_params[:token]) - elsif params[:template] == 'delete' - return true if @domain.registrant_delete_confirmable?(verify_params[:token]) - end + action = if params[:template] == 'change' + @domain.registrant_update_confirmable?(verify_params[:token]) + elsif params[:template] == 'delete' + @domain.registrant_delete_confirmable?(verify_params[:token]) + end + + return unless action render json: { error: 'Application expired or not found' }, status: :unauthorized end From 60a66bc540d30487e58886576e7d1e283c8ae2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 15:46:55 +0200 Subject: [PATCH 112/172] Registrant confirms API: Add tests --- .../api/v1/registrant/confirms_controller.rb | 39 ++- .../registrant_api_verifications_test.rb | 259 ++++++++++++++++++ 2 files changed, 283 insertions(+), 15 deletions(-) create mode 100644 test/integration/api/registrant/registrant_api_verifications_test.rb diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index d03e7ab93..057400c8e 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -10,11 +10,16 @@ module Api before_action :verify_decision, only: %i[update] def index - render json: { + res = { domain_name: @domain.name, current_registrant: serialized_registrant(@domain.registrant), - new_registrant: serialized_registrant(@domain.pending_registrant), } + + unless delete_action? + res[:new_registrant] = serialized_registrant(@domain.pending_registrant) + end + + render json: res, status: :ok end def update @@ -28,7 +33,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(current_registrant), - status: params[:decision] } + status: params[:decision] }, status: :ok end private @@ -38,23 +43,27 @@ module Api end def current_registrant - approved? ? @domain.registrant : @domain.pending_registrant + confirmed? && !delete_action? ? @domain.pending_registrant : @domain.registrant end - def approved? - params[:decision] == 'confirmed' + def confirmed? + verify_params[:decision] == 'confirmed' end def change_action(verification) - return verification.domain_registrant_change_confirm!(initiator) if approved? - - verification.domain_registrant_change_reject!(initiator) + if confirmed? + verification.domain_registrant_change_confirm!(initiator) + else + verification.domain_registrant_change_reject!(initiator) + end end def delete_action(verification) - return verification.domain_registrant_delete_confirm!(initiator) if approved? - - verification.domain_registrant_delete_reject!(initiator) + if confirmed? + verification.domain_registrant_delete_confirm!(initiator) + else + verification.domain_registrant_delete_reject!(initiator) + end end def serialized_registrant(registrant) @@ -67,9 +76,9 @@ module Api def verify_params params do |p| - p.require(:template) p.require(:name) p.require(:token) + p.permit(:decision) end end @@ -82,7 +91,7 @@ module Api def verify_decision return if %w[confirmed rejected].include?(params[:decision]) - head :bad_request + head :not_found end def set_domain @@ -100,7 +109,7 @@ module Api @domain.registrant_delete_confirmable?(verify_params[:token]) end - return unless action + return if action render json: { error: 'Application expired or not found' }, status: :unauthorized end diff --git a/test/integration/api/registrant/registrant_api_verifications_test.rb b/test/integration/api/registrant/registrant_api_verifications_test.rb new file mode 100644 index 000000000..b2333e560 --- /dev/null +++ b/test/integration/api/registrant/registrant_api_verifications_test.rb @@ -0,0 +1,259 @@ +require 'test_helper' +require 'auth_token/auth_token_creator' + +class RegistrantApiVerificationsTest < ApplicationIntegrationTest + def setup + super + + @domain = domains(:hospital) + @registrant = @domain.registrant + @new_registrant = contacts(:jack) + + @token = 'verysecrettoken' + + @domain.update(statuses: [DomainStatus::PENDING_UPDATE], + registrant_verification_asked_at: Time.zone.now - 1.day, + registrant_verification_token: @token) + + end + + def test_fetches_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: "hospital.test", + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + new_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + } + } + + assert_equal expected_body, res + end + + def test_approves_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + }, + status: 'confirmed' + } + + assert_equal expected_body, res + end + + def test_rejects_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/rejected" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'rejected' + } + + assert_equal expected_body, res + end + + def test_registrant_change_requires_valid_attributes + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/change/123" + assert_equal 401, response.status + + get "/api/v1/registrant/confirms/aohldfjg.ee/change/123" + assert_equal 404, response.status + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/invalidaction" + assert_equal 404, response.status + end + + def test_fetches_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: "hospital.test", + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + } + } + + assert_equal expected_body, res + end + + def test_approves_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/confirmed" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'confirmed' + } + + assert_equal expected_body, res + end + + def test_rejects_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/rejected" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'rejected' + } + + assert_equal expected_body, res + end + + def test_domain_delete_requires_valid_attributes + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE]) + @domain.reload + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/123" + assert_equal 401, response.status + + get "/api/v1/registrant/confirms/aohldfjg.ee/delete/123" + assert_equal 404, response.status + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/invalidaction" + assert_equal 404, response.status + end + #def test_get_non_existent_domain_details_by_uuid + # get '/api/v1/registrant/domains/random-uuid', headers: @auth_headers + # assert_equal(404, response.status) + + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [base: ['Domain not found']] }, response_json) + #end + + #def test_root_returns_domain_list + # get '/api/v1/registrant/domains', headers: @auth_headers + # assert_equal(200, response.status) + + # response_json = JSON.parse(response.body, symbolize_names: true) + # array_of_domain_names = response_json.map { |x| x[:name] } + # assert(array_of_domain_names.include?('hospital.test')) + + # array_of_domain_registrars = response_json.map { |x| x[:registrar] } + # assert(array_of_domain_registrars.include?({name: 'Good Names', website: nil})) + #end + + #def test_root_accepts_limit_and_offset_parameters + # get '/api/v1/registrant/domains', params: { 'limit' => 2, 'offset' => 0 }, + # headers: @auth_headers + # response_json = JSON.parse(response.body, symbolize_names: true) + + # assert_equal(200, response.status) + # assert_equal(2, response_json.count) + + # get '/api/v1/registrant/domains', headers: @auth_headers + # response_json = JSON.parse(response.body, symbolize_names: true) + + # assert_equal(4, response_json.count) + #end + + #def test_root_does_not_accept_limit_higher_than_200 + # get '/api/v1/registrant/domains', params: { 'limit' => 400, 'offset' => 0 }, + # headers: @auth_headers + + # assert_equal(400, response.status) + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [{ limit: ['parameter is out of range'] }] }, response_json) + #end + + #def test_root_does_not_accept_offset_lower_than_0 + # get '/api/v1/registrant/domains', params: { 'limit' => 200, 'offset' => "-10" }, + # headers: @auth_headers + + # assert_equal(400, response.status) + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [{ offset: ['parameter is out of range'] }] }, response_json) + #end + + #def test_root_returns_401_without_authorization + # get '/api/v1/registrant/domains' + # assert_equal(401, response.status) + # json_body = JSON.parse(response.body, symbolize_names: true) + + # assert_equal({ errors: [base: ['Not authorized']] }, json_body) + #end + + #def test_details_returns_401_without_authorization + # get '/api/v1/registrant/domains/5edda1a5-3548-41ee-8b65-6d60daf85a37' + # assert_equal(401, response.status) + # json_body = JSON.parse(response.body, symbolize_names: true) + + # assert_equal({ errors: [base: ['Not authorized']] }, json_body) + #end +end From 26ec7e3f12a3d88294b8631c57ed2c1c95b60a5e Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 16:55:40 +0300 Subject: [PATCH 113/172] Combine tests --- .../integration/epp/login/credentials_test.rb | 64 ------- .../epp/login/password_change_test.rb | 32 ---- .../epp/login/session_limit_test.rb | 61 ------- test/integration/epp/login_test.rb | 167 ++++++++++++++++++ 4 files changed, 167 insertions(+), 157 deletions(-) delete mode 100644 test/integration/epp/login/credentials_test.rb delete mode 100644 test/integration/epp/login/password_change_test.rb delete mode 100644 test/integration/epp/login/session_limit_test.rb create mode 100644 test/integration/epp/login_test.rb diff --git a/test/integration/epp/login/credentials_test.rb b/test/integration/epp/login/credentials_test.rb deleted file mode 100644 index 0f7dac97c..000000000 --- a/test/integration/epp/login/credentials_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'test_helper' - -class EppLoginCredentialsTest < EppTestCase - def test_correct_credentials - request_xml = <<-XML - - - - - test_bestnames - testtest - - 1.0 - en - - - https://epp.tld.ee/schema/domain-eis-1.0.xsd - https://epp.tld.ee/schema/contact-ee-1.1.xsd - urn:ietf:params:xml:ns:host-1.0 - - - - - XML - - post epp_login_path, params: { frame: request_xml }, - headers: { 'HTTP_COOKIE' => 'session=new_session_id' } - assert EppSession.find_by(session_id: 'new_session_id') - assert_equal users(:api_bestnames), EppSession.find_by(session_id: 'new_session_id').user - assert_epp_response :completed_successfully - end - - def test_already_logged_in - assert true # Handled by EPP proxy - end - - def test_wrong_credentials - request_xml = <<-XML - - - - - non-existent - valid-but-wrong - - 1.0 - en - - - https://epp.tld.ee/schema/domain-eis-1.0.xsd - https://epp.tld.ee/schema/contact-ee-1.1.xsd - urn:ietf:params:xml:ns:host-1.0 - - - - - XML - - post epp_login_path, params: { frame: request_xml }, - headers: { 'HTTP_COOKIE' => 'session=any_random_string' } - - assert_epp_response :authentication_error_server_closing_connection - end -end diff --git a/test/integration/epp/login/password_change_test.rb b/test/integration/epp/login/password_change_test.rb deleted file mode 100644 index 3b1834406..000000000 --- a/test/integration/epp/login/password_change_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test_helper' - -class EppLoginPasswordChangeTest < EppTestCase - def test_password_change - request_xml = <<-XML - - - - - test_bestnames - testtest - new-password - - 1.0 - en - - - https://epp.tld.ee/schema/domain-eis-1.0.xsd - https://epp.tld.ee/schema/contact-ee-1.1.xsd - urn:ietf:params:xml:ns:host-1.0 - - - - - XML - - post epp_login_path, params: { frame: request_xml }, - headers: { 'HTTP_COOKIE' => 'session=new_session_id' } - assert_equal 'new-password', users(:api_bestnames).plain_text_password - assert_epp_response :completed_successfully - end -end \ No newline at end of file diff --git a/test/integration/epp/login/session_limit_test.rb b/test/integration/epp/login/session_limit_test.rb deleted file mode 100644 index f1d83fe7b..000000000 --- a/test/integration/epp/login/session_limit_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test_helper' - -class EppLoginSessionLimitTest < EppTestCase - setup do - travel_to Time.zone.parse('2010-07-05') - EppSession.delete_all - end - - def test_not_reached - (EppSession.limit_per_registrar - 1).times do - EppSession.create!(session_id: SecureRandom.hex, - user: users(:api_bestnames), - updated_at: Time.zone.parse('2010-07-05')) - end - - assert_difference 'EppSession.count' do - post epp_login_path, params: { frame: request_xml }, - headers: { 'HTTP_COOKIE' => 'session=new_session_id' } - end - assert_epp_response :completed_successfully - end - - def test_reached - EppSession.limit_per_registrar.times do - EppSession.create!(session_id: SecureRandom.hex, - user: users(:api_bestnames), - updated_at: Time.zone.parse('2010-07-05')) - end - - assert_no_difference 'EppSession.count' do - post epp_login_path, params: { frame: request_xml }, - headers: { 'HTTP_COOKIE' => 'session=new_session_id' } - end - assert_epp_response :authentication_error_server_closing_connection - end - - private - - def request_xml - <<-XML - - - - - test_bestnames - testtest - - 1.0 - en - - - https://epp.tld.ee/schema/domain-eis-1.0.xsd - https://epp.tld.ee/schema/contact-ee-1.1.xsd - urn:ietf:params:xml:ns:host-1.0 - - - - - XML - end -end diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb new file mode 100644 index 000000000..d3ef22ec1 --- /dev/null +++ b/test/integration/epp/login_test.rb @@ -0,0 +1,167 @@ +require 'test_helper' + +class EppLoginTest < EppTestCase + def test_correct_credentials + request_xml = <<-XML + + + + + test_bestnames + testtest + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + + post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + assert EppSession.find_by(session_id: 'new_session_id') + assert_equal users(:api_bestnames), EppSession.find_by(session_id: 'new_session_id').user + assert_epp_response :completed_successfully + end + + def test_already_logged_in + assert true # Handled by mod_epp + end + + def test_wrong_credentials + request_xml = <<-XML + + + + + non-existent + valid-but-wrong + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=any_random_string' + + assert_epp_response :authentication_error_server_closing_connection + end + + def test_password_change + request_xml = <<-XML + + + + + test_bestnames + testtest + new-password + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + + post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + assert_equal 'new-password', users(:api_bestnames).plain_text_password + assert_epp_response :completed_successfully + end + + def test_not_reached + travel_to Time.zone.parse('2010-07-05') + EppSession.delete_all + request_xml = <<-XML + + + + + test_bestnames + testtest + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + + (EppSession.limit_per_registrar - 1).times do + EppSession.create!(session_id: SecureRandom.hex, + user: users(:api_bestnames), + updated_at: Time.zone.parse('2010-07-05')) + end + + assert_difference 'EppSession.count' do + post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + end + assert_epp_response :completed_successfully + end + + def test_reached + travel_to Time.zone.parse('2010-07-05') + EppSession.delete_all + request_xml = <<-XML + + + + + test_bestnames + testtest + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + + EppSession.limit_per_registrar.times do + EppSession.create!(session_id: SecureRandom.hex, + user: users(:api_bestnames), + updated_at: Time.zone.parse('2010-07-05')) + end + + assert_no_difference 'EppSession.count' do + post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + end + assert_epp_response :authentication_error_server_closing_connection + end +end From 087300ff9d440e9c18759288184ee8337490223b Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 17:01:29 +0300 Subject: [PATCH 114/172] Reformat --- app/controllers/epp/sessions_controller.rb | 6 +++--- test/integration/epp/login_test.rb | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index b1c7fbbfb..e84659c86 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -90,8 +90,8 @@ module Epp if params[:parsed_frame].css('newPW').first unless @api_user.update(plain_text_password: params[:parsed_frame].css('newPW').first.text) handle_errors(@api_user) and return + end end - end epp_session = EppSession.new epp_session.session_id = epp_session_id @@ -100,8 +100,8 @@ module Epp render_epp_response('login_success') else handle_errors + end end - end def ip_white? webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip) @@ -125,7 +125,7 @@ module Epp @api_user = current_user # cache current_user for logging epp_session.destroy render_epp_response('logout') - end + end ### HELPER METHODS ### diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index d3ef22ec1..2c69dcf49 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -23,11 +23,11 @@ class EppLoginTest < EppTestCase XML + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' - post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + assert_epp_response :completed_successfully assert EppSession.find_by(session_id: 'new_session_id') assert_equal users(:api_bestnames), EppSession.find_by(session_id: 'new_session_id').user - assert_epp_response :completed_successfully end def test_already_logged_in @@ -56,7 +56,6 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=any_random_string' assert_epp_response :authentication_error_server_closing_connection @@ -85,8 +84,8 @@ class EppLoginTest < EppTestCase XML + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' - post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } assert_equal 'new-password', users(:api_bestnames).plain_text_password assert_epp_response :completed_successfully end @@ -123,7 +122,7 @@ class EppLoginTest < EppTestCase end assert_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' end assert_epp_response :completed_successfully end @@ -160,7 +159,7 @@ class EppLoginTest < EppTestCase end assert_no_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=new_session_id' } + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' end assert_epp_response :authentication_error_server_closing_connection end From 7ba5b3b2ae74992aa1a4112848c37911d40d27b5 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 17:10:08 +0300 Subject: [PATCH 115/172] Refactor EPP login password change --- app/controllers/epp/sessions_controller.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index e84659c86..6c3509786 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -87,10 +87,11 @@ module Epp end if success - if params[:parsed_frame].css('newPW').first - unless @api_user.update(plain_text_password: params[:parsed_frame].css('newPW').first.text) - handle_errors(@api_user) and return - end + new_password = params[:parsed_frame].at_css('newPW')&.text + + if new_password.present? + @api_user.plain_text_password = new_password + @api_user.save! end epp_session = EppSession.new From 3a5779782a111442b8cf6610e02a0f842106cc5d Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 17:53:32 +0300 Subject: [PATCH 116/172] Prohibit authenticated EPP user from logging in again Fixes #1313 --- app/controllers/epp/sessions_controller.rb | 14 +++++++++- test/integration/epp/login_test.rb | 30 ++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 6c3509786..df706b55d 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -88,12 +88,24 @@ module Epp if success new_password = params[:parsed_frame].at_css('newPW')&.text + password_change = new_password.present? - if new_password.present? + if password_change @api_user.plain_text_password = new_password @api_user.save! end + already_authenticated = EppSession.exists?(session_id: epp_session_id) + + if already_authenticated + epp_errors << { + msg: 'Command use error; Already authenticated', + code: 2002, + } + handle_errors + return + end + epp_session = EppSession.new epp_session.session_id = epp_session_id epp_session.user = @api_user diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index 2c69dcf49..c44ac1eee 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -30,8 +30,34 @@ class EppLoginTest < EppTestCase assert_equal users(:api_bestnames), EppSession.find_by(session_id: 'new_session_id').user end - def test_already_logged_in - assert true # Handled by mod_epp + def test_user_cannot_login_again + session = epp_sessions(:api_bestnames) + user = session.user + + request_xml = <<-XML + + + + + #{user.username} + #{user.plain_text_password} + + 1.0 + en + + + https://epp.tld.ee/schema/domain-eis-1.0.xsd + https://epp.tld.ee/schema/contact-ee-1.1.xsd + urn:ietf:params:xml:ns:host-1.0 + urn:ietf:params:xml:ns:keyrelay-1.0 + + + + + XML + post '/epp/session/login', { frame: request_xml }, HTTP_COOKIE: "session=#{session.session_id}" + + assert_epp_response :use_error end def test_wrong_credentials From 370b37cff6f479a49e3d8e21d3931817b6db4775 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 18:22:30 +0300 Subject: [PATCH 117/172] Improve readability --- test/integration/epp/login_test.rb | 65 +++++++++++++++++++----------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index c44ac1eee..83e41cc27 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -1,14 +1,17 @@ require 'test_helper' class EppLoginTest < EppTestCase - def test_correct_credentials + def test_logging_in_with_correct_credentials_creates_new_session + user = users(:api_bestnames) + new_session_id = 'new-session-id' + request_xml = <<-XML - test_bestnames - testtest + #{user.username} + #{user.plain_text_password} 1.0 en @@ -23,11 +26,13 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' - + assert_difference 'EppSession.count' do + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => "session=#{new_session_id}" + end assert_epp_response :completed_successfully - assert EppSession.find_by(session_id: 'new_session_id') - assert_equal users(:api_bestnames), EppSession.find_by(session_id: 'new_session_id').user + session = EppSession.last + assert_equal new_session_id, session.session_id + assert_equal user, session.user end def test_user_cannot_login_again @@ -55,19 +60,25 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, HTTP_COOKIE: "session=#{session.session_id}" + assert_no_difference 'EppSession.count' do + post '/epp/session/login', { frame: request_xml }, HTTP_COOKIE: "session=#{session.session_id}" + end assert_epp_response :use_error end - def test_wrong_credentials + def test_user_cannot_login_with_wrong_credentials + user = users(:api_bestnames) + wrong_password = 'a' * ApiUser.min_password_length + assert_not_equal wrong_password, user.plain_text_password + request_xml = <<-XML - non-existent - valid-but-wrong + #{user.username} + #{wrong_password} 1.0 en @@ -82,20 +93,26 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=any_random_string' + assert_no_difference 'EppSession.count' do + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' + end assert_epp_response :authentication_error_server_closing_connection end def test_password_change + user = users(:api_bestnames) + new_password = 'a' * ApiUser.min_password_length + assert_not_equal new_password, user.plain_text_password + request_xml = <<-XML - test_bestnames - testtest - new-password + #{user.username} + #{user.plain_text_password} + #{new_password} 1.0 en @@ -110,10 +127,11 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' + user.reload - assert_equal 'new-password', users(:api_bestnames).plain_text_password assert_epp_response :completed_successfully + assert_equal new_password, user.plain_text_password end def test_not_reached @@ -148,12 +166,13 @@ class EppLoginTest < EppTestCase end assert_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=non-existent' end assert_epp_response :completed_successfully end - def test_reached + def test_user_cannot_login_when_session_limit_reached + user = users(:api_bestnames) travel_to Time.zone.parse('2010-07-05') EppSession.delete_all request_xml = <<-XML @@ -161,8 +180,8 @@ class EppLoginTest < EppTestCase - test_bestnames - testtest + #{user.username} + #{user.plain_text_password} 1.0 en @@ -180,12 +199,12 @@ class EppLoginTest < EppTestCase EppSession.limit_per_registrar.times do EppSession.create!(session_id: SecureRandom.hex, - user: users(:api_bestnames), + user: user, updated_at: Time.zone.parse('2010-07-05')) end assert_no_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new_session_id' + post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' end assert_epp_response :authentication_error_server_closing_connection end From 744d6a2b537b4e3f70d429f886f1fd382d44f644 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 18:23:18 +0300 Subject: [PATCH 118/172] Remove unnecessary test --- test/integration/epp/login_test.rb | 37 ------------------------------ 1 file changed, 37 deletions(-) diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index 83e41cc27..fe8ef2cf8 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -134,43 +134,6 @@ class EppLoginTest < EppTestCase assert_equal new_password, user.plain_text_password end - def test_not_reached - travel_to Time.zone.parse('2010-07-05') - EppSession.delete_all - request_xml = <<-XML - - - - - test_bestnames - testtest - - 1.0 - en - - - https://epp.tld.ee/schema/domain-eis-1.0.xsd - https://epp.tld.ee/schema/contact-ee-1.1.xsd - urn:ietf:params:xml:ns:host-1.0 - urn:ietf:params:xml:ns:keyrelay-1.0 - - - - - XML - - (EppSession.limit_per_registrar - 1).times do - EppSession.create!(session_id: SecureRandom.hex, - user: users(:api_bestnames), - updated_at: Time.zone.parse('2010-07-05')) - end - - assert_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=non-existent' - end - assert_epp_response :completed_successfully - end - def test_user_cannot_login_when_session_limit_reached user = users(:api_bestnames) travel_to Time.zone.parse('2010-07-05') From daeb00ebe7719689703cee1ad5d469e01cef4ffe Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 13 Sep 2019 21:06:23 +0300 Subject: [PATCH 119/172] Change EPP response code according to its specification Fixes #587 --- app/controllers/epp/sessions_controller.rb | 4 ++-- app/models/epp/response/result/code.rb | 2 ++ test/integration/epp/login_test.rb | 2 +- test/models/epp/response/result/code_test.rb | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index df706b55d..04603dbe7 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -79,8 +79,8 @@ module Epp if success && EppSession.limit_reached?(@api_user.registrar) epp_errors << { - msg: 'Authentication error; server closing connection (connection limit reached)', - code: '2501' + msg: 'Session limit exceeded; server closing connection (connection limit reached)', + code: '2502', } success = false diff --git a/app/models/epp/response/result/code.rb b/app/models/epp/response/result/code.rb index 1be4a3f7c..10edf0a35 100644 --- a/app/models/epp/response/result/code.rb +++ b/app/models/epp/response/result/code.rb @@ -30,6 +30,7 @@ module Epp data_management_policy_violation: 2308, command_failed: 2400, authentication_error_server_closing_connection: 2501, + session_limit_exceeded_server_closing_connection: 2502, }.freeze private_constant :KEY_TO_VALUE @@ -59,6 +60,7 @@ module Epp 2308 => 'Data management policy violation', 2400 => 'Command failed', 2501 => 'Authentication error; server closing connection', + 2502 => 'Session limit exceeded; server closing connection', }.freeze private_constant :DEFAULT_DESCRIPTIONS diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index fe8ef2cf8..7442728a0 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -169,6 +169,6 @@ class EppLoginTest < EppTestCase assert_no_difference 'EppSession.count' do post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' end - assert_epp_response :authentication_error_server_closing_connection + assert_epp_response :session_limit_exceeded_server_closing_connection end end diff --git a/test/models/epp/response/result/code_test.rb b/test/models/epp/response/result/code_test.rb index f16013180..184a18438 100644 --- a/test/models/epp/response/result/code_test.rb +++ b/test/models/epp/response/result/code_test.rb @@ -51,6 +51,7 @@ class EppResponseResultCodeTest < ActiveSupport::TestCase data_management_policy_violation: 2308, command_failed: 2400, authentication_error_server_closing_connection: 2501, + session_limit_exceeded_server_closing_connection: 2502, } assert_equal codes, Epp::Response::Result::Code.codes end @@ -82,6 +83,7 @@ class EppResponseResultCodeTest < ActiveSupport::TestCase 2308 => 'Data management policy violation', 2400 => 'Command failed', 2501 => 'Authentication error; server closing connection', + 2502 => 'Session limit exceeded; server closing connection', } assert_equal descriptions, Epp::Response::Result::Code.default_descriptions end From 59fbcde4ca6356e334ec5b36823d4dd84060dfdd Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 17 Sep 2019 17:17:51 +0300 Subject: [PATCH 120/172] Remove hardcoded value --- app/models/epp_session.rb | 7 +++---- config/application.yml.sample | 2 ++ test/models/epp_session_test.rb | 4 ---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/models/epp_session.rb b/app/models/epp_session.rb index f1b641aa0..1336be39e 100644 --- a/app/models/epp_session.rb +++ b/app/models/epp_session.rb @@ -6,11 +6,10 @@ class EppSession < ApplicationRecord class_attribute :timeout self.timeout = (ENV['epp_session_timeout_seconds'] || 300).to_i.seconds - alias_attribute :last_access, :updated_at + class_attribute :limit_per_registrar + self.limit_per_registrar = (ENV['epp_session_limit_per_registrar'] || 4).to_i - def self.limit_per_registrar - 4 - end + alias_attribute :last_access, :updated_at def self.limit_reached?(registrar) count = where(user_id: registrar.api_users.ids).where('updated_at >= ?', Time.zone.now - 1.second).count diff --git a/config/application.yml.sample b/config/application.yml.sample index ab64ed35e..65e83b603 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -170,6 +170,8 @@ tara_rant_redirect_uri: 'redirect_uri' default_email_validation_type: 'regex' +epp_session_limit_per_registrar: '4' + # Since the keys for staging are absent from the repo, we need to supply them separate for testing. test: payments_seb_bank_certificate: 'test/fixtures/files/seb_bank_cert.pem' diff --git a/test/models/epp_session_test.rb b/test/models/epp_session_test.rb index 8ed63f6ab..398753bd1 100644 --- a/test/models/epp_session_test.rb +++ b/test/models/epp_session_test.rb @@ -49,10 +49,6 @@ class EppSessionTest < ActiveSupport::TestCase end end - def test_limit_per_registrar - assert_equal 4, EppSession.limit_per_registrar - end - def test_limit_is_per_registrar travel_to Time.zone.parse('2010-07-05') EppSession.delete_all From 2d1e11ac6d7d93ba31f119640c7fd0810203c79c Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 17 Sep 2019 17:39:58 +0300 Subject: [PATCH 121/172] Improve readability --- test/integration/epp/login_test.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index 7442728a0..bd49f591d 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -134,10 +134,16 @@ class EppLoginTest < EppTestCase assert_equal new_password, user.plain_text_password end - def test_user_cannot_login_when_session_limit_reached + def test_user_cannot_login_when_session_limit_is_exceeded user = users(:api_bestnames) travel_to Time.zone.parse('2010-07-05') - EppSession.delete_all + eliminate_effect_of_existing_epp_sessions + EppSession.limit_per_registrar.times do + EppSession.create!(session_id: SecureRandom.hex, + user: user, + updated_at: Time.zone.parse('2010-07-05')) + end + request_xml = <<-XML @@ -160,15 +166,15 @@ class EppLoginTest < EppTestCase XML - EppSession.limit_per_registrar.times do - EppSession.create!(session_id: SecureRandom.hex, - user: user, - updated_at: Time.zone.parse('2010-07-05')) - end - assert_no_difference 'EppSession.count' do post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' end assert_epp_response :session_limit_exceeded_server_closing_connection end + + private + + def eliminate_effect_of_existing_epp_sessions + EppSession.delete_all + end end From 1e997ad5b1ff39f05eb1ab3f61e5868851fd9bd1 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 17 Sep 2019 17:50:02 +0300 Subject: [PATCH 122/172] Simplify test --- test/integration/epp/login_test.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index bd49f591d..ada2ff0c3 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -1,6 +1,14 @@ require 'test_helper' class EppLoginTest < EppTestCase + setup do + @original_session_limit_per_registrar = EppSession.limit_per_registrar + end + + teardown do + EppSession.limit_per_registrar = @original_session_limit_per_registrar + end + def test_logging_in_with_correct_credentials_creates_new_session user = users(:api_bestnames) new_session_id = 'new-session-id' @@ -138,11 +146,10 @@ class EppLoginTest < EppTestCase user = users(:api_bestnames) travel_to Time.zone.parse('2010-07-05') eliminate_effect_of_existing_epp_sessions - EppSession.limit_per_registrar.times do - EppSession.create!(session_id: SecureRandom.hex, - user: user, - updated_at: Time.zone.parse('2010-07-05')) - end + EppSession.limit_per_registrar = 1 + EppSession.create!(session_id: 'any', + user: user, + updated_at: Time.zone.parse('2010-07-05')) request_xml = <<-XML From 9eb7d3527b8bbc9175cbd53e9889eb6d99ffe611 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 17 Sep 2019 17:55:24 +0300 Subject: [PATCH 123/172] Do not take time into account when checking EPP session limit --- app/models/epp_session.rb | 2 +- test/integration/epp/login_test.rb | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/models/epp_session.rb b/app/models/epp_session.rb index 1336be39e..657f76973 100644 --- a/app/models/epp_session.rb +++ b/app/models/epp_session.rb @@ -12,7 +12,7 @@ class EppSession < ApplicationRecord alias_attribute :last_access, :updated_at def self.limit_reached?(registrar) - count = where(user_id: registrar.api_users.ids).where('updated_at >= ?', Time.zone.now - 1.second).count + count = where(user_id: registrar.api_users.ids).count count >= limit_per_registrar end diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index ada2ff0c3..fe62f9afe 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -144,12 +144,9 @@ class EppLoginTest < EppTestCase def test_user_cannot_login_when_session_limit_is_exceeded user = users(:api_bestnames) - travel_to Time.zone.parse('2010-07-05') eliminate_effect_of_existing_epp_sessions EppSession.limit_per_registrar = 1 - EppSession.create!(session_id: 'any', - user: user, - updated_at: Time.zone.parse('2010-07-05')) + EppSession.create!(session_id: 'any', user: user) request_xml = <<-XML From cbb3c0d5ef706c63102cd9e1e311d24aab162621 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 17 Sep 2019 18:03:49 +0300 Subject: [PATCH 124/172] Improve readability --- app/models/epp_session.rb | 6 +++--- config/application.yml.sample | 2 +- test/integration/epp/login_test.rb | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/epp_session.rb b/app/models/epp_session.rb index 657f76973..7567bf3d2 100644 --- a/app/models/epp_session.rb +++ b/app/models/epp_session.rb @@ -6,14 +6,14 @@ class EppSession < ApplicationRecord class_attribute :timeout self.timeout = (ENV['epp_session_timeout_seconds'] || 300).to_i.seconds - class_attribute :limit_per_registrar - self.limit_per_registrar = (ENV['epp_session_limit_per_registrar'] || 4).to_i + class_attribute :sessions_per_registrar + self.sessions_per_registrar = (ENV['epp_session_limit_per_registrar'] || 4).to_i alias_attribute :last_access, :updated_at def self.limit_reached?(registrar) count = where(user_id: registrar.api_users.ids).count - count >= limit_per_registrar + count >= sessions_per_registrar end def self.expired diff --git a/config/application.yml.sample b/config/application.yml.sample index 65e83b603..e979e772a 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -170,7 +170,7 @@ tara_rant_redirect_uri: 'redirect_uri' default_email_validation_type: 'regex' -epp_session_limit_per_registrar: '4' +epp_sessions_per_registrar: '4' # Since the keys for staging are absent from the repo, we need to supply them separate for testing. test: diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index fe62f9afe..d82ba8891 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -2,11 +2,11 @@ require 'test_helper' class EppLoginTest < EppTestCase setup do - @original_session_limit_per_registrar = EppSession.limit_per_registrar + @original_sessions_per_registrar_setting = EppSession.sessions_per_registrar end teardown do - EppSession.limit_per_registrar = @original_session_limit_per_registrar + EppSession.sessions_per_registrar = @original_sessions_per_registrar_setting end def test_logging_in_with_correct_credentials_creates_new_session @@ -142,10 +142,10 @@ class EppLoginTest < EppTestCase assert_equal new_password, user.plain_text_password end - def test_user_cannot_login_when_session_limit_is_exceeded + def test_user_cannot_login_when_max_allowed_sessions_per_registrar_is_exceeded user = users(:api_bestnames) eliminate_effect_of_existing_epp_sessions - EppSession.limit_per_registrar = 1 + EppSession.sessions_per_registrar = 1 EppSession.create!(session_id: 'any', user: user) request_xml = <<-XML From 26c8fe6a3e8e68a75ba3076e1d329d50f5ea0d4a Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 4 Sep 2020 15:06:40 +0500 Subject: [PATCH 125/172] Fix tests --- test/integration/epp/login_test.rb | 15 ++++++++++----- test/models/epp_session_test.rb | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/test/integration/epp/login_test.rb b/test/integration/epp/login_test.rb index d82ba8891..dd7a750c9 100644 --- a/test/integration/epp/login_test.rb +++ b/test/integration/epp/login_test.rb @@ -35,7 +35,8 @@ class EppLoginTest < EppTestCase XML assert_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => "session=#{new_session_id}" + post '/epp/session/login', params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => "session=#{new_session_id}" } end assert_epp_response :completed_successfully session = EppSession.last @@ -70,7 +71,8 @@ class EppLoginTest < EppTestCase XML assert_no_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, HTTP_COOKIE: "session=#{session.session_id}" + post '/epp/session/login', params: { frame: request_xml }, + headers: { HTTP_COOKIE: "session=#{session.session_id}" } end assert_epp_response :use_error end @@ -103,7 +105,8 @@ class EppLoginTest < EppTestCase XML assert_no_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' + post '/epp/session/login', params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=new-session-id' } end assert_epp_response :authentication_error_server_closing_connection end @@ -135,7 +138,8 @@ class EppLoginTest < EppTestCase XML - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' + post '/epp/session/login', params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=new-session-id' } user.reload assert_epp_response :completed_successfully @@ -171,7 +175,8 @@ class EppLoginTest < EppTestCase XML assert_no_difference 'EppSession.count' do - post '/epp/session/login', { frame: request_xml }, 'HTTP_COOKIE' => 'session=new-session-id' + post '/epp/session/login', params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=new-session-id' } end assert_epp_response :session_limit_exceeded_server_closing_connection end diff --git a/test/models/epp_session_test.rb b/test/models/epp_session_test.rb index 398753bd1..3fd184e0b 100644 --- a/test/models/epp_session_test.rb +++ b/test/models/epp_session_test.rb @@ -53,7 +53,7 @@ class EppSessionTest < ActiveSupport::TestCase travel_to Time.zone.parse('2010-07-05') EppSession.delete_all - EppSession.limit_per_registrar.times do + EppSession.sessions_per_registrar.times do EppSession.create!(session_id: SecureRandom.hex, user: users(:api_goodnames), updated_at: Time.zone.parse('2010-07-05')) From 495e1a4ae169a7f200b63e49087cbd3ce0448e71 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 13 Nov 2020 13:36:53 +0500 Subject: [PATCH 126/172] Fix env usage in EppSession model --- app/models/epp_session.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/epp_session.rb b/app/models/epp_session.rb index 7567bf3d2..6bec39ee3 100644 --- a/app/models/epp_session.rb +++ b/app/models/epp_session.rb @@ -7,7 +7,7 @@ class EppSession < ApplicationRecord self.timeout = (ENV['epp_session_timeout_seconds'] || 300).to_i.seconds class_attribute :sessions_per_registrar - self.sessions_per_registrar = (ENV['epp_session_limit_per_registrar'] || 4).to_i + self.sessions_per_registrar = (ENV['epp_sessions_per_registrar'] || 4).to_i alias_attribute :last_access, :updated_at From 13b0d45e255ff44834407b00e77f7ffac0057ae0 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 13 Nov 2020 14:38:24 +0500 Subject: [PATCH 127/172] Check for 4 sessions only in inexpired ones --- app/models/epp_session.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/models/epp_session.rb b/app/models/epp_session.rb index 6bec39ee3..a6fb97ed2 100644 --- a/app/models/epp_session.rb +++ b/app/models/epp_session.rb @@ -11,13 +11,21 @@ class EppSession < ApplicationRecord alias_attribute :last_access, :updated_at + scope :not_expired, + lambda { + where(':now <= (updated_at + interval :interval)', now: Time.zone.now, interval: interval) + } + def self.limit_reached?(registrar) - count = where(user_id: registrar.api_users.ids).count + count = where(user_id: registrar.api_users.ids).not_expired.count count >= sessions_per_registrar end + def self.interval + "#{timeout.parts.first.second} #{timeout.parts.first.first}" + end + def self.expired - interval = "#{timeout.parts.first.second} #{timeout.parts.first.first}" where(':now > (updated_at + interval :interval)', now: Time.zone.now, interval: interval) end From 54eb1e91decdc65d880ae1f44d0ca6ab135f1f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 17:05:13 +0200 Subject: [PATCH 128/172] Fix CC issues --- app/mailers/application_mailer.rb | 13 ++- app/mailers/domain_delete_mailer.rb | 11 +-- app/mailers/registrant_change_mailer.rb | 11 +-- .../registrant_api_verifications_test.rb | 90 +++++++++++++------ 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 9269a1102..9366174ef 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,15 @@ class ApplicationMailer < ActionMailer::Base append_view_path Rails.root.join('app', 'views', 'mailers') layout 'mailer' -end \ No newline at end of file + + def registrant_confirm_url(domain:, method:) + token = domain.registrant_verification_token + base_url = ENV['registrant_portal_verifications_base_url'] + + url = registrant_domain_delete_confirm_url(domain, token: token) if method == 'delete' + url ||= registrant_domain_update_confirm_url(domain, token: token) + return url if base_url.blank? + + "#{base_url}/confirms/#{domain.name_puny}/#{method}/#{token}" + end +end diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index 8e2b1a341..8c0e830b1 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -6,7 +6,7 @@ class DomainDeleteMailer < ApplicationMailer def confirmation_request(domain:, registrar:, registrant:) @domain = DomainPresenter.new(domain: domain, view: view_context) @registrar = RegistrarPresenter.new(registrar: registrar, view: view_context) - @confirmation_url = confirmation_url(domain) + @confirmation_url = registrant_confirm_url(domain: domain, method: 'delete') subject = default_i18n_subject(domain_name: domain.name) mail(to: registrant.email, subject: subject) @@ -52,15 +52,6 @@ class DomainDeleteMailer < ApplicationMailer private - def confirmation_url(domain) - base_url = ENV['registrant_portal_verifications_base_url'] - if base_url.blank? - registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) - else - "#{base_url}/confirmation/#{domain.name_puny}/delete/#{domain.registrant_verification_token}" - end - end - def forced_email_from ENV['action_mailer_force_delete_from'] || self.class.default[:from] end diff --git a/app/mailers/registrant_change_mailer.rb b/app/mailers/registrant_change_mailer.rb index 3e97f4b86..8f43f4ab5 100644 --- a/app/mailers/registrant_change_mailer.rb +++ b/app/mailers/registrant_change_mailer.rb @@ -5,7 +5,7 @@ class RegistrantChangeMailer < ApplicationMailer @domain = DomainPresenter.new(domain: domain, view: view_context) @registrar = RegistrarPresenter.new(registrar: registrar, view: view_context) @new_registrant = RegistrantPresenter.new(registrant: new_registrant, view: view_context) - @confirmation_url = confirmation_url(domain) + @confirmation_url = registrant_confirm_url(domain: domain, method: 'change') subject = default_i18n_subject(domain_name: domain.name) mail(to: current_registrant.email, subject: subject) @@ -49,15 +49,6 @@ class RegistrantChangeMailer < ApplicationMailer private - def confirmation_url(domain) - base_url = ENV['registrant_portal_verifications_base_url'] - if base_url.blank? - registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) - else - "#{base_url}/confirmation/#{domain.name_puny}/change/#{domain.registrant_verification_token}" - end - end - def address_processing Contact.address_processing? end diff --git a/test/integration/api/registrant/registrant_api_verifications_test.rb b/test/integration/api/registrant/registrant_api_verifications_test.rb index b2333e560..821d0dee0 100644 --- a/test/integration/api/registrant/registrant_api_verifications_test.rb +++ b/test/integration/api/registrant/registrant_api_verifications_test.rb @@ -8,17 +8,22 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest @domain = domains(:hospital) @registrant = @domain.registrant @new_registrant = contacts(:jack) + @user = users(:api_bestnames) @token = 'verysecrettoken' - @domain.update(statuses: [DomainStatus::PENDING_UPDATE], + @domain.update!(statuses: [DomainStatus::PENDING_UPDATE], registrant_verification_asked_at: Time.zone.now - 1.day, registrant_verification_token: @token) end def test_fetches_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -46,31 +51,40 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_approves_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } - @domain.update(pending_json: pending_json) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update!(pending_json: pending_json) @domain.reload assert @domain.registrant_update_confirmable?(@token) - post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" - assert_equal(200, response.status) + perform_enqueued_jobs do + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" + assert_equal(200, response.status) - res = JSON.parse(response.body, symbolize_names: true) - expected_body = { - domain_name: @domain.name, - current_registrant: { - name: @new_registrant.name, - ident: @new_registrant.ident, - country: @new_registrant.ident_country_code - }, - status: 'confirmed' - } - - assert_equal expected_body, res + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + }, + status: 'confirmed' + } + assert_equal expected_body, res + end end def test_rejects_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -94,7 +108,11 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_registrant_change_requires_valid_attributes - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -109,7 +127,12 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_fetches_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -131,8 +154,13 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_approves_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - @domain.reload + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -154,8 +182,13 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_rejects_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - @domain.reload + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -177,7 +210,12 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_domain_delete_requires_valid_attributes - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE]) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) @domain.reload get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/123" From fc816ad67ba143804764ddaeca8721d6e4accbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 13 Nov 2020 16:38:39 +0200 Subject: [PATCH 129/172] REPP: Check for ident hash presence before contact update --- app/controllers/repp/v1/contacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index a6cd7d07d..504948ed9 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -108,7 +108,7 @@ module Repp end def contact_ident_params(required: true) - if required + if required || !params[:contact][:ident].nil? params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else From 331ade988c2e891d8addc634e105bf68318defc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 13 Nov 2020 17:00:44 +0200 Subject: [PATCH 130/172] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f710645..58ae259b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +13.11.2020 +* Fixed per registrar epp session limit [#729](https://github.com/internetee/registry/issues/729) +* Correct error code is returned on reaching session limit [#587](https://github.com/internetee/registry/issues/587) +* No logins within active session [#1313](https://github.com/internetee/registry/issues/1313) + 06.11.2020 * Csv option to limit list of domains for bulk nameserver change in registrar portal [#1737](https://github.com/internetee/registry/issues/1737) * New forceDelete email template for invalid contact data [#1178](https://github.com/internetee/registry/issues/1178) From 3d0150076c83eba56d474f89557750f8879ba9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 16 Nov 2020 14:46:37 +0200 Subject: [PATCH 131/172] REPP: Hide contact internal ID --- .../repp/v1/contacts_controller.rb | 16 ++++--- app/models/epp/contact.rb | 2 +- lib/serializers/repp/contact.rb | 42 +++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 lib/serializers/repp/contact.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 504948ed9..69d4ecc96 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,3 +1,4 @@ +require 'serializers/repp/contact' module Repp module V1 class ContactsController < BaseController @@ -14,7 +15,8 @@ module Repp ## GET /repp/v1/contacts/1 def show - render_success(data: @contact.as_json) + serializer = ::Serializers::Repp::Contact.new(@contact, show_address: Contact.address_processing?) + render_success(data: serializer.to_json) end ## GET /repp/v1/contacts/check/1 @@ -76,11 +78,13 @@ module Repp def showable_contacts(details, limit, offset) contacts = current_user.registrar.contacts.limit(limit).offset(offset) - unless Contact.address_processing? && params[:details] == 'true' - contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) - end - contacts = contacts.pluck(:code) unless details + return contacts.pluck(:code) unless details + + contacts = contacts.map do |contact| + serializer = ::Serializers::Repp::Contact.new(contact, show_address: Contact.address_processing?) + serializer.to_json + end contacts end @@ -104,7 +108,7 @@ module Repp def contact_create_params(required: true) params.require(:contact).require(%i[name email phone]) if required - params.require(:contact).permit(:name, :email, :phone, :code) + params.require(:contact).permit(:name, :email, :phone, :id) end def contact_ident_params(required: true) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 3f0f3e8ab..50ebac065 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -36,7 +36,7 @@ class Epp::Contact < Contact attrs = epp ? attrs_from(frame, new_record: true) : frame super( attrs.merge( - code: epp ? frame.css('id').text : frame[:code], + code: epp ? frame.css('id').text : frame[:id], registrar: registrar ) ) diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb new file mode 100644 index 000000000..fe08a6f7d --- /dev/null +++ b/lib/serializers/repp/contact.rb @@ -0,0 +1,42 @@ +module Serializers + module Repp + class Contact + attr_reader :contact + + def initialize(contact, show_address:) + @contact = contact + @show_address = show_address + end + + def to_json + json = { + id: contact.code, + name: contact.name, + ident: { + code: contact.ident, + type: contact.ident_type, + country_code: contact.ident_country_code, + }, + email: contact.email, + phone: contact.phone, + fax: contact.fax, + auth_info: contact.auth_info, + statuses: contact.statuses, + disclosed_attributes: contact.disclosed_attributes, + } + + return json unless @show_address + + json[:address] = { + street: contact.street, + zip: contact.zip, + city: contact.city, + state: contact.state, + country_code: contact.country_code, + } + + json + end + end + end +end From 03db562c77f9020dad4b61a3bba80b6baa0fa93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 16 Nov 2020 16:26:59 +0200 Subject: [PATCH 132/172] REPP: Show error when invalid ident format submitted --- app/controllers/repp/v1/base_controller.rb | 1 + app/controllers/repp/v1/contacts_controller.rb | 2 +- app/models/actions/contact_update.rb | 8 ++++++-- test/integration/repp/v1/contacts/show_test.rb | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 678ae0a22..70b189fc1 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,6 +4,7 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction + before_action :set_paper_trail_whodunnit attr_reader :current_user rescue_from ActionController::ParameterMissing do |exception| diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 69d4ecc96..959d4f6c7 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -112,7 +112,7 @@ module Repp end def contact_ident_params(required: true) - if required || !params[:contact][:ident].nil? + if required params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else diff --git a/app/models/actions/contact_update.rb b/app/models/actions/contact_update.rb index 6400526c5..7ca7b6b04 100644 --- a/app/models/actions/contact_update.rb +++ b/app/models/actions/contact_update.rb @@ -17,7 +17,7 @@ module Actions def call maybe_remove_address maybe_update_statuses - maybe_update_ident if ident + maybe_update_ident if ident.present? maybe_attach_legal_doc commit end @@ -53,7 +53,11 @@ module Actions end def maybe_update_ident - return unless ident[: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], diff --git a/test/integration/repp/v1/contacts/show_test.rb b/test/integration/repp/v1/contacts/show_test.rb index 3fd782cca..4a6f5b615 100644 --- a/test/integration/repp/v1/contacts/show_test.rb +++ b/test/integration/repp/v1/contacts/show_test.rb @@ -28,7 +28,7 @@ class ReppV1ContactsShowTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert_equal contact.code, json[:data][:code] + assert_equal contact.code, json[:data][:id] end def test_can_not_access_out_of_scope_contacts From 7edf48c885e6bced344f125c8e8304a04866f0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 11:51:45 +0200 Subject: [PATCH 133/172] Fix some CC issues --- .../repp/v1/contacts_controller.rb | 6 ++- .../v1/registrar/nameservers_controller.rb | 2 +- lib/serializers/registrant_api/.DS_Store | Bin 0 -> 6148 bytes lib/serializers/repp/contact.rb | 44 ++++++++---------- 4 files changed, 24 insertions(+), 28 deletions(-) create mode 100644 lib/serializers/registrant_api/.DS_Store diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 959d4f6c7..144be01c6 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -15,7 +15,8 @@ module Repp ## GET /repp/v1/contacts/1 def show - serializer = ::Serializers::Repp::Contact.new(@contact, show_address: Contact.address_processing?) + serializer = ::Serializers::Repp::Contact.new(@contact, + show_address: Contact.address_processing?) render_success(data: serializer.to_json) end @@ -82,7 +83,8 @@ module Repp return contacts.pluck(:code) unless details contacts = contacts.map do |contact| - serializer = ::Serializers::Repp::Contact.new(contact, show_address: Contact.address_processing?) + serializer = ::Serializers::Repp::Contact.new(contact, + show_address: Contact.address_processing?) serializer.to_json end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index fb00d92c0..47004d97b 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -34,7 +34,7 @@ module Repp params.permit(data: [ :type, :id, { domains: [], - attributes: [:hostname, { ipv4: [], ipv6: [] }] }, + attributes: [:hostname, { ipv4: [], ipv6: [] }] } ]) end diff --git a/lib/serializers/registrant_api/.DS_Store b/lib/serializers/registrant_api/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Tue, 17 Nov 2020 13:00:47 +0200 Subject: [PATCH 134/172] Add response to REPP log --- .../repp/v1/auctions_controller.rb | 4 +-- app/controllers/repp/v1/base_controller.rb | 29 +++++++------------ .../repp/v1/contacts_controller.rb | 4 +-- .../repp/v1/domains/contacts_controller.rb | 4 +-- .../repp/v1/retained_domains_controller.rb | 3 +- lib/serializers/repp/contact.rb | 10 +++---- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/app/controllers/repp/v1/auctions_controller.rb b/app/controllers/repp/v1/auctions_controller.rb index 4a5265d13..676dac266 100644 --- a/app/controllers/repp/v1/auctions_controller.rb +++ b/app/controllers/repp/v1/auctions_controller.rb @@ -3,9 +3,9 @@ module Repp class AuctionsController < ActionController::API def index auctions = Auction.started + @response = { count: auctions.count, auctions: auctions_to_json(auctions) } - render json: { count: auctions.count, - auctions: auctions_to_json(auctions) } + render json: @response end private diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 70b189fc1..73d8c400a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -24,10 +24,10 @@ module Repp private def render_success(code: nil, message: nil, data: nil) - resp = { code: code || 1000, message: message || 'Command completed successfully', + @response = { code: code || 1000, message: message || 'Command completed successfully', data: data || {} } - render(json: resp, status: :ok) + render(json: @response, status: :ok) end def epp_errors @@ -64,10 +64,8 @@ module Repp @epp_errors ||= [] @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} - render( - json: { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data }, - status: status - ) + @response = { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data } + render(json: @response, status: status) end def basic_token @@ -85,10 +83,8 @@ module Repp raise(ArgumentError) rescue NoMethodError, ArgumentError - render( - json: { code: 2202, message: 'Invalid authorization information' }, - status: :unauthorized - ) + @response = { code: 2202, message: 'Invalid authorization information' } + render(json: @response, status: :unauthorized) end def check_ip_restriction @@ -96,17 +92,14 @@ module Repp return if allowed - render( - json: { - code: 2202, - message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip), - }, - status: :unauthorized - ) + @response = { code: 2202, + message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip) } + render(json: @response, status: :unauthorized) end def not_found_error - render(json: { code: 2303, message: 'Object does not exist' }, status: :not_found) + @response = { code: 2303, message: 'Object does not exist' } + render(json: @response, status: :not_found) end end end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 144be01c6..acf275c47 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -9,8 +9,8 @@ module Repp record_count = current_user.registrar.contacts.count contacts = showable_contacts(params[:details], params[:limit] || 200, params[:offset] || 0) - - render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) + @response = { contacts: contacts, total_number_of_records: record_count } + render(json: @response, status: :ok) end ## GET /repp/v1/contacts/1 diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index fb5dfb567..75404e0c6 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -26,8 +26,8 @@ module Repp return handle_errors if @epp_errors.any? affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) - data = { affected_domains: affected, skipped_domains: skipped } - render_success(data: data) + @response = { affected_domains: affected, skipped_domains: skipped } + render_success(data: @response) end private diff --git a/app/controllers/repp/v1/retained_domains_controller.rb b/app/controllers/repp/v1/retained_domains_controller.rb index c1bb458e9..8edc32f5b 100644 --- a/app/controllers/repp/v1/retained_domains_controller.rb +++ b/app/controllers/repp/v1/retained_domains_controller.rb @@ -3,8 +3,9 @@ module Repp class RetainedDomainsController < ActionController::API def index domains = RetainedDomains.new(query_params) + @response = { count: domains.count, domains: domains.to_jsonable } - render json: { count: domains.count, domains: domains.to_jsonable } + render json: @response end def query_params diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb index 8a3cad616..834402359 100644 --- a/lib/serializers/repp/contact.rb +++ b/lib/serializers/repp/contact.rb @@ -8,11 +8,11 @@ module Serializers @show_address = show_address end - def to_json(_obj) - json = { id: contact.code, name: contact.name, ident: ident, - email: contact.email, phone: contact.phone, fax: contact.fax, - auth_info: contact.auth_info, statuses: contact.statuses, - disclosed_attributes: contact.disclosed_attributes } + def to_json(obj = contact) + json = { id: obj.code, name: obj.name, ident: ident, + email: obj.email, phone: obj.phone, fax: obj.fax, + auth_info: obj.auth_info, statuses: obj.statuses, + disclosed_attributes: obj.disclosed_attributes } json[:address] = address if @show_address From e40737408f9fec6eaf1400f31a530936e457247c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 14:33:12 +0200 Subject: [PATCH 135/172] History: Show last state of contact on contact destroy event --- app/helpers/object_versions_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index d8e00abbe..8e394b29a 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -3,7 +3,8 @@ module ObjectVersionsHelper version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, value.last) + delete_action = version.event == 'destroy' + new_object.public_send(method_name, delete_action ? value.first : value.last) end end end From c9ebdbeb40e1f4377e30ee5ca0cdf733ead11554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 14:40:18 +0200 Subject: [PATCH 136/172] EPP: Hide name from contactInfo if no auth provided --- app/views/epp/contacts/info.xml.builder | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 1945e7def..4874080e8 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -14,7 +14,11 @@ xml.epp_head do end xml.tag!('contact:postalInfo', type: 'int') do - xml.tag!('contact:name', @contact.name) + if can? :view_full_info, @contact, @password + xml.tag!('contact:name', @contact.name) + else + xml.tag!('contact:name', 'No access') + end if can? :view_full_info, @contact, @password xml.tag!('contact:org', @contact.org_name) if @contact.org_name.present? From a747464f3d8d076aaebce2246111c4d47e370c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 16:16:27 +0200 Subject: [PATCH 137/172] Don't show contact name if not viewed by sponsoring registrar --- app/views/registrar/domains/partials/_contacts.haml | 2 +- app/views/registrar/domains/partials/_general.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/registrar/domains/partials/_contacts.haml b/app/views/registrar/domains/partials/_contacts.haml index 48d1ac21f..e6ef9aa8f 100644 --- a/app/views/registrar/domains/partials/_contacts.haml +++ b/app/views/registrar/domains/partials/_contacts.haml @@ -13,5 +13,5 @@ - registrant = Contact.find_by_code(x.text) %tr %td= x['type'] - %td= registrant.name + %td= registrant.registrar == current_registrar_user.registrar ? registrant.name : 'N/A' %td= x.text diff --git a/app/views/registrar/domains/partials/_general.html.erb b/app/views/registrar/domains/partials/_general.html.erb index 3fb3a5df8..ff064857c 100644 --- a/app/views/registrar/domains/partials/_general.html.erb +++ b/app/views/registrar/domains/partials/_general.html.erb @@ -23,7 +23,7 @@ <% registrant = Contact.find_by_code(@data.css('registrant').text) %>
<%= t('.registrant') %>
-
<%= "#{registrant.name} (#{@data.css('registrant').text})" %>
+
<%= registrant.registrar == current_registrar_user.registrar ? "#{registrant.name} (#{@data.css('registrant').text})" : @data.css('registrant').text %>
<%= t('.registered') %>
<%= @data.css('crDate').text %>
From a6b6a3e60ad3319073c355bce51472a4362dc1f1 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 17 Nov 2020 19:37:24 +0500 Subject: [PATCH 138/172] Enable WhiteIp for checking against subnets --- app/models/white_ip.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 303ff5886..e86f62ea7 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -24,7 +24,10 @@ class WhiteIp < ApplicationRecord class << self def include_ip?(ip) - where('ipv4 = :ip OR ipv6 = :ip', ip: ip).any? + ipv4 = select{ |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select{ |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4.pluck(:id) + ipv6.pluck(:id)).flatten.uniq + where(id: ids).any? end end end From 5cc85980d448cf71cd1bb2ef218bc2b9ef5138a0 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 17 Nov 2020 19:46:10 +0500 Subject: [PATCH 139/172] Fixed validations --- app/models/white_ip.rb | 28 ++++++++++++++++++++----- test/system/registrar_area/base_test.rb | 10 +++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index e86f62ea7..c2fb51cd1 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -2,8 +2,8 @@ class WhiteIp < ApplicationRecord include Versions belongs_to :registrar - validates :ipv4, format: { with: /\A(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/, allow_blank: true } - validates :ipv6, format: { with: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, allow_blank: true } + validate :valid_ipv4? + validate :valid_ipv6? validate :validate_ipv4_and_ipv6 def validate_ipv4_and_ipv6 @@ -11,6 +11,22 @@ class WhiteIp < ApplicationRecord errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present)) end + def valid_ipv4? + return if ipv4.blank? + + IPAddr.new(ipv4, Socket::AF_INET) + rescue StandardError => _e + errors.add(:ipv4, :invalid) + end + + def valid_ipv6? + return if ipv6.blank? + + IPAddr.new(ipv6, Socket::AF_INET6) + rescue StandardError => _e + errors.add(:ipv6, :invalid) + end + API = 'api' REGISTRAR = 'registrar' INTERFACES = [API, REGISTRAR] @@ -23,11 +39,13 @@ class WhiteIp < ApplicationRecord end class << self + # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select{ |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select{ |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4.pluck(:id) + ipv6.pluck(:id)).flatten.uniq + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4 + ipv6).pluck(:id).flatten.uniq where(id: ids).any? end + # rubocop:enable Style/CaseEquality end end diff --git a/test/system/registrar_area/base_test.rb b/test/system/registrar_area/base_test.rb index 67b19a044..2529b009a 100644 --- a/test/system/registrar_area/base_test.rb +++ b/test/system/registrar_area/base_test.rb @@ -29,6 +29,16 @@ class RegistrarAreaBaseTestTest < ApplicationSystemTestCase assert_button 'Login' end + def test_user_can_access_when_ip_is_whitelisted_with_subnet + white_ips(:one).update!(ipv4: '127.0.0.1/32', interfaces: [WhiteIp::REGISTRAR]) + Setting.registrar_ip_whitelist_enabled = true + + visit new_registrar_user_session_url + + assert_no_text 'Access denied from IP 127.0.0.1' + assert_button 'Login' + end + def test_user_can_access_when_ip_is_not_whitelisted_and_whitelist_is_disabled Setting.registrar_ip_whitelist_enabled = false WhiteIp.delete_all From 7d08e22f6fe1ca0f6419907303d36fcdd3925d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 16:55:07 +0200 Subject: [PATCH 140/172] Fix CC issues --- app/controllers/repp/v1/base_controller.rb | 2 +- app/helpers/object_versions_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 73d8c400a..4bfa61338 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -25,7 +25,7 @@ module Repp def render_success(code: nil, message: nil, data: nil) @response = { code: code || 1000, message: message || 'Command completed successfully', - data: data || {} } + data: data || {} } render(json: @response, status: :ok) end diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index 8e394b29a..fcec7c7d9 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -1,10 +1,10 @@ module ObjectVersionsHelper def attach_existing_fields(version, new_object) + destroy_event = version.event == 'destroy' version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - delete_action = version.event == 'destroy' - new_object.public_send(method_name, delete_action ? value.first : value.last) + new_object.public_send(method_name, destroy_event ? value.first : value.last) end end end From 56b5d8e275727080d27835a82bbe7be9e1c1ecb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 10:56:57 +0200 Subject: [PATCH 141/172] Fix registrant verificication URL building --- app/mailers/application_mailer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 9366174ef..af5d59d63 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -10,6 +10,6 @@ class ApplicationMailer < ActionMailer::Base url ||= registrant_domain_update_confirm_url(domain, token: token) return url if base_url.blank? - "#{base_url}/confirms/#{domain.name_puny}/#{method}/#{token}" + "#{base_url}/confirmation/#{domain.name_puny}/#{method}/#{token}" end end From 38f86225ca16a294e1deacd8f4383059a724f922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 12:35:11 +0200 Subject: [PATCH 142/172] Add tests, show papertrail whodunnit properly on repp api --- app/controllers/repp/v1/base_controller.rb | 7 ++++++- app/helpers/object_versions_helper.rb | 9 +++++++-- test/integration/repp/v1/contacts/update_test.rb | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 4bfa61338..ca9ef6fb5 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,9 +4,10 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction - before_action :set_paper_trail_whodunnit attr_reader :current_user + before_action :set_paper_trail_whodunnit + rescue_from ActionController::ParameterMissing do |exception| render json: { code: 2003, message: exception }, status: :bad_request end @@ -23,6 +24,10 @@ module Repp private + def set_paper_trail_whodunnit + ::PaperTrail.request.whodunnit = current_user + end + def render_success(code: nil, message: nil, data: nil) @response = { code: code || 1000, message: message || 'Command completed successfully', data: data || {} } diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index fcec7c7d9..dccf03017 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -1,10 +1,9 @@ module ObjectVersionsHelper def attach_existing_fields(version, new_object) - destroy_event = version.event == 'destroy' version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, destroy_event ? value.first : value.last) + new_object.public_send(method_name, event_value(event, value)) end end end @@ -13,4 +12,10 @@ module ObjectVersionsHelper field_names = model.column_names version.object.to_h.select { |key, _value| field_names.include?(key) } end + + private + + def event_value(version, val) + version.event == 'destroy' ? val.first : val.last + end end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb index dd762e341..cf27f98da 100644 --- a/test/integration/repp/v1/contacts/update_test.rb +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -101,4 +101,19 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest @contact.reload assert @contact.legal_documents.any? end + + def test_returns_error_if_ident_wrong_format + request_body = { + "contact": { + "ident": "123" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2308, json[:code] + assert_equal 'Ident update is not allowed. Consider creating new contact object', json[:message] + end end From c7d64dce37c3dc5498c142a07e6963bbcab9b829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 13:08:28 +0200 Subject: [PATCH 143/172] EPP contactInfo: add test for name masking --- test/integration/epp/contact/info/base_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/epp/contact/info/base_test.rb b/test/integration/epp/contact/info/base_test.rb index 80dad97e8..4e4a9190e 100644 --- a/test/integration/epp/contact/info/base_test.rb +++ b/test/integration/epp/contact/info/base_test.rb @@ -44,7 +44,7 @@ class EppContactInfoBaseTest < EppTestCase contact: xml_schema).text end - def test_hides_password_when_current_registrar_is_not_sponsoring + def test_hides_password_and_name_when_current_registrar_is_not_sponsoring non_sponsoring_registrar = registrars(:goodnames) @contact.update!(registrar: non_sponsoring_registrar) @@ -70,6 +70,7 @@ class EppContactInfoBaseTest < EppTestCase assert_epp_response :completed_successfully response_xml = Nokogiri::XML(response.body) assert_nil response_xml.at_xpath('//contact:authInfo', contact: xml_schema) + assert_equal 'No access', response_xml.at_xpath('//contact:name', contact: xml_schema).text end private From 3d01cbb6aab4b59d065583e6fee0683dba6b6878 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 19 Nov 2020 13:40:49 +0500 Subject: [PATCH 144/172] Add test to be sure if emails are sent --- test/models/domain/force_delete_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 2c264d916..7c205fa74 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -1,18 +1,20 @@ require 'test_helper' -class NewDomainForceDeleteTest < ActiveSupport::TestCase +class ForceDeleteTest < ActionMailer::TestCase setup do @domain = domains(:shop) Setting.redemption_grace_period = 30 + ActionMailer::Base.deliveries.clear end def test_schedules_force_delete_fast_track assert_not @domain.force_delete_scheduled? travel_to Time.zone.parse('2010-07-05') - @domain.schedule_force_delete(type: :fast_track) + @domain.schedule_force_delete(type: :fast_track, notify_by_email: true) @domain.reload + assert_emails 1 assert @domain.force_delete_scheduled? assert_equal Date.parse('2010-08-20'), @domain.force_delete_date.to_date assert_equal Date.parse('2010-07-06'), @domain.force_delete_start.to_date From 0dea44dcd3c77a300f122950c0760313d2cf821e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 16:55:23 +0200 Subject: [PATCH 145/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ae259b2..e2cf1cb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +19.11.2020 +* Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) + 13.11.2020 * Fixed per registrar epp session limit [#729](https://github.com/internetee/registry/issues/729) * Correct error code is returned on reaching session limit [#587](https://github.com/internetee/registry/issues/587) From 3c3eaa960483e8acbeffa19883f65b7f59d37345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:21:18 +0200 Subject: [PATCH 146/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2cf1cb33..913d3cc21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) +* Refactor ForceDelete [#1740](https://github.com/internetee/registry/issues/1740) 13.11.2020 * Fixed per registrar epp session limit [#729](https://github.com/internetee/registry/issues/729) From 86b86491de42fba2078ef993a1681eb9aa45d8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:23:22 +0200 Subject: [PATCH 147/172] Update expired_soft.text.erb --- app/views/mailers/domain_expire_mailer/expired_soft.text.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.text.erb b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb index 0e6d9c953..7be32f73b 100644 --- a/app/views/mailers/domain_expire_mailer/expired_soft.text.erb +++ b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb @@ -2,7 +2,7 @@ Domeen <%= @domain.name %> on aegunud ning suunatud kustutusmenetlusse kuna olem Lugupeetud .ee domeeni registreerija/halduskontakt -Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole. +Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulikke kontakti objekte, millest tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole. <%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit https://www.internet.ee/domeenioksjonid. From a5b06a7da7218aa8facec448b9d6d65d7c7b7d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:24:03 +0200 Subject: [PATCH 148/172] Update expired_soft.html.erb --- app/views/mailers/domain_expire_mailer/expired_soft.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.html.erb b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb index 1dd661a38..0bcfc6acd 100644 --- a/app/views/mailers/domain_expire_mailer/expired_soft.html.erb +++ b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb @@ -2,7 +2,7 @@

Lugupeetud .ee domeeni registreerija/halduskontakt

-

Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole.

+

Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulikke kontakti objekte, millest tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole.

<%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit.

From 6a6265e3a5b97918558ae4218119061ce665679c Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 18 Nov 2020 18:09:31 +0500 Subject: [PATCH 149/172] Move CancelForceDelete to interactor --- .../cancel_force_delete_interaction/base.rb | 7 +++++ .../cancel_force_delete.rb | 10 +++++++ .../clear_force_delete_data.rb | 10 +++++++ .../notify_registrar.rb | 8 ++++++ .../remove_force_delete_statuses.rb | 11 ++++++++ .../restore_statuses_before_force_delete.rb | 9 +++++++ app/models/concerns/domain/force_delete.rb | 26 +------------------ 7 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 app/interactions/cancel_force_delete_interaction/base.rb create mode 100644 app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb create mode 100644 app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb create mode 100644 app/interactions/cancel_force_delete_interaction/notify_registrar.rb create mode 100644 app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb create mode 100644 app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb diff --git a/app/interactions/cancel_force_delete_interaction/base.rb b/app/interactions/cancel_force_delete_interaction/base.rb new file mode 100644 index 000000000..595279d87 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/base.rb @@ -0,0 +1,7 @@ +module CancelForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to cancel ForceDelete on' + end +end diff --git a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb new file mode 100644 index 000000000..2f45bf0e4 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb @@ -0,0 +1,10 @@ +module CancelForceDeleteInteraction + class CancelForceDelete < Base + def execute + compose(RemoveForceDeleteStatuses, inputs) + compose(RestoreStatusesBeforeForceDelete, inputs) + compose(ClearForceDeleteData, inputs) + compose(NotifyRegistrar, inputs) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb new file mode 100644 index 000000000..ccc0714f7 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb @@ -0,0 +1,10 @@ +module CancelForceDeleteInteraction + class ClearForceDeleteData < Base + def execute + domain.force_delete_data = nil + domain.force_delete_date = nil + domain.force_delete_start = nil + domain.save(validate: false) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb new file mode 100644 index 000000000..3ded4c489 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb @@ -0,0 +1,8 @@ +module CancelForceDeleteInteraction + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', + domain_name: domain.name)) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb new file mode 100644 index 000000000..1cc4989e5 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb @@ -0,0 +1,11 @@ +module CancelForceDeleteInteraction + class RemoveForceDeleteStatuses < Base + def execute + domain.statuses.delete(DomainStatus::FORCE_DELETE) + domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) + domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) + domain.statuses.delete(DomainStatus::CLIENT_HOLD) + domain.save(validate: false) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb new file mode 100644 index 000000000..06b6bbd18 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb @@ -0,0 +1,9 @@ +module CancelForceDeleteInteraction + class RestoreStatusesBeforeForceDelete < Base + def execute + domain.statuses = domain.statuses_before_force_delete + domain.statuses_before_force_delete = nil + domain.save(validate: false) + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index b119b0ce0..ff869fc0a 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -58,18 +58,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength notify_by_email: notify_by_email) end - def clear_force_delete_data - self.force_delete_data = nil - end - def cancel_force_delete - remove_force_delete_statuses - restore_statuses_before_force_delete - clear_force_delete_data - self.force_delete_date = nil - self.force_delete_start = nil - save(validate: false) - registrar.notifications.create!(text: I18n.t('force_delete_cancelled', domain_name: name)) + CancelForceDeleteInteraction::CancelForceDelete.run(domain: self) end def outzone_date @@ -80,18 +70,4 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength (force_delete_date&.beginning_of_day || valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days) end - - private - - def restore_statuses_before_force_delete - self.statuses = statuses_before_force_delete - self.statuses_before_force_delete = nil - end - - def remove_force_delete_statuses - statuses.delete(DomainStatus::FORCE_DELETE) - statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) - statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) - statuses.delete(DomainStatus::CLIENT_HOLD) - end end From 822f19a676f02561419aa5e0d99dc5b7de794a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 20 Nov 2020 13:48:07 +0200 Subject: [PATCH 150/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 913d3cc21..52408c420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +20.11.2020 +* Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) + 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) * Refactor ForceDelete [#1740](https://github.com/internetee/registry/issues/1740) From fce16f924d02e5f6cd89c0845efd2794e6db65f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 20 Nov 2020 17:28:40 +0200 Subject: [PATCH 151/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52408c420..28810c8d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) +* Refactored forceDelete cancellation for interactors [#1743](https://github.com/internetee/registry/issues/1743) 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) From a40116e05e5e80378ef99f861ec824c020ffd276 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 20 Nov 2020 16:51:05 +0500 Subject: [PATCH 152/172] Move ClientHold procedure to interactor Closes #1751 --- .../client_hold_interaction/base.rb | 8 +++ .../process_client_hold.rb | 67 +++++++++++++++++++ .../set_client_hold.rb | 15 +++++ app/models/concerns/domain/force_delete.rb | 19 ------ app/models/concerns/job/force_delete.rb | 34 ---------- .../concerns/job/force_delete_logging.rb | 34 ---------- .../concerns/job/force_delete_notify.rb | 31 --------- app/models/domain_cron.rb | 8 +-- test/models/domain/force_delete_test.rb | 13 ++-- 9 files changed, 102 insertions(+), 127 deletions(-) create mode 100644 app/interactions/client_hold_interaction/base.rb create mode 100644 app/interactions/client_hold_interaction/process_client_hold.rb create mode 100644 app/interactions/client_hold_interaction/set_client_hold.rb delete mode 100644 app/models/concerns/job/force_delete.rb delete mode 100644 app/models/concerns/job/force_delete_logging.rb delete mode 100644 app/models/concerns/job/force_delete_notify.rb diff --git a/app/interactions/client_hold_interaction/base.rb b/app/interactions/client_hold_interaction/base.rb new file mode 100644 index 000000000..be8b8b0a8 --- /dev/null +++ b/app/interactions/client_hold_interaction/base.rb @@ -0,0 +1,8 @@ +module ClientHoldInteraction + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end +end diff --git a/app/interactions/client_hold_interaction/process_client_hold.rb b/app/interactions/client_hold_interaction/process_client_hold.rb new file mode 100644 index 000000000..453c400cc --- /dev/null +++ b/app/interactions/client_hold_interaction/process_client_hold.rb @@ -0,0 +1,67 @@ +module ClientHoldInteraction + class ProcessClientHold < Base + object :domain, + class: Domain, + description: 'Domain to set ClientHold on' + + # rubocop:disable Metrics/AbcSize + def execute + notify_on_grace_period if should_notify_on_soft_force_delete? + + return unless client_holdable? + + domain.statuses << DomainStatus::CLIENT_HOLD + to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") + + domain.save(validate: false) + notify_client_hold + + to_stdout("Successfully set client_hold on (#{domain.name})") + end + + def notify_on_grace_period + domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', + domain_name: domain.name, + date: domain.force_delete_start)) + send_mail if domain.template_name.present? + domain.update(contact_notification_sent_date: Time.zone.today) + end + + def notify_client_hold + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + + def send_mail + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.template_name).deliver_now + end + + def should_notify_on_soft_force_delete? + domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && + domain.force_delete_start.to_date <= Time.zone.now.to_date && + domain.force_delete_type.to_sym == :soft && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) + end + # rubocop:enable Metrics/AbcSize + + def client_holdable? + domain.force_delete_scheduled? && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && + domain.force_delete_start.present? && + force_delete_lte_today && force_delete_lte_valid_date + end + + def force_delete_lte_today + domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now + end + + def force_delete_lte_valid_date + domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to + end + end +end diff --git a/app/interactions/client_hold_interaction/set_client_hold.rb b/app/interactions/client_hold_interaction/set_client_hold.rb new file mode 100644 index 000000000..d723d5cc8 --- /dev/null +++ b/app/interactions/client_hold_interaction/set_client_hold.rb @@ -0,0 +1,15 @@ +module ClientHoldInteraction + class SetClientHold < Base + def execute + to_stdout('Setting client_hold to domains\n') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + ::Domain.force_delete_scheduled.each do |domain| + ClientHoldInteraction::ProcessClientHold.run(domain: domain) + end + + to_stdout('All client_hold setting are done\n') + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index ff869fc0a..988c8075a 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -33,25 +33,6 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength statuses.include?(DomainStatus::FORCE_DELETE) end - def should_notify_on_soft_force_delete? - force_delete_scheduled? && contact_notification_sent_date.blank? && - force_delete_start.to_date <= Time.zone.now.to_date && force_delete_type.to_sym == :soft && - !statuses.include?(DomainStatus::CLIENT_HOLD) - end - - def client_holdable? - force_delete_scheduled? && !statuses.include?(DomainStatus::CLIENT_HOLD) && - force_delete_start.present? && force_delete_lte_today && force_delete_lte_valid_date - end - - def force_delete_lte_today - force_delete_start + Setting.expire_warning_period.days <= Time.zone.now - end - - def force_delete_lte_valid_date - force_delete_start + Setting.expire_warning_period.days <= valid_to - end - def schedule_force_delete(type: :fast_track, notify_by_email: false) ForceDeleteInteraction::SetForceDelete.run(domain: self, type: type, diff --git a/app/models/concerns/job/force_delete.rb b/app/models/concerns/job/force_delete.rb deleted file mode 100644 index 316612d1e..000000000 --- a/app/models/concerns/job/force_delete.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Concerns - module Job - module ForceDelete - extend ActiveSupport::Concern - - class_methods do - def start_client_hold - log_prepare_client_hold - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - - ::Domain.force_delete_scheduled.each do |domain| - proceed_client_hold(domain: domain) - end - - log_end_end_force_delete_job - end - - def proceed_client_hold(domain:) - notify_on_grace_period(domain) if domain.should_notify_on_soft_force_delete? - return unless domain.client_holdable? - - domain.statuses << DomainStatus::CLIENT_HOLD - log_start_client_hold(domain) - - domain.save(validate: false) - notify_client_hold(domain) - - log_end_end_client_hold(domain) - end - end - end - end -end diff --git a/app/models/concerns/job/force_delete_logging.rb b/app/models/concerns/job/force_delete_logging.rb deleted file mode 100644 index 8f6ee227c..000000000 --- a/app/models/concerns/job/force_delete_logging.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Concerns - module Job - module ForceDeleteLogging - extend ActiveSupport::Concern - - class_methods do - def log_prepare_client_hold - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - Setting client_hold to domains\n" - end - - def log_start_client_hold(domain) - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} DomainCron.start_client_hold: ##{domain.id} "\ - "(#{domain.name}) #{domain.changes}\n" - end - - def log_end_end_client_hold(domain) - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - Successfully set client_hold on (#{domain.name})" - end - - def log_end_end_force_delete_job - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - All client_hold setting are done\n" - end - end - end - end -end diff --git a/app/models/concerns/job/force_delete_notify.rb b/app/models/concerns/job/force_delete_notify.rb deleted file mode 100644 index bc291354e..000000000 --- a/app/models/concerns/job/force_delete_notify.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Concerns - module Job - module ForceDeleteNotify - extend ActiveSupport::Concern - - class_methods do - def notify_client_hold(domain) - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - - def notify_on_grace_period(domain) - domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', - domain_name: domain.name, - date: domain.force_delete_start)) - send_mail(domain) if domain.template_name.present? - domain.update(contact_notification_sent_date: Time.zone.today) - end - - def send_mail(domain) - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.template_name).deliver_now - end - end - end - end -end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index ad64456ca..613762062 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -1,8 +1,4 @@ class DomainCron - include Concerns::Job::ForceDelete - include Concerns::Job::ForceDeleteLogging - include Concerns::Job::ForceDeleteNotify - def self.clean_expired_pendings STDOUT << "#{Time.zone.now.utc} - Clean expired domain pendings\n" unless Rails.env.test? @@ -81,4 +77,8 @@ class DomainCron STDOUT << "#{Time.zone.now.utc} - Successfully set server_hold to #{marked} of #{real} domains\n" unless Rails.env.test? marked end + + def self.start_client_hold + ClientHoldInteraction::SetClientHold.run! + end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 7c205fa74..d9e7a3632 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -206,9 +206,10 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload + assert_emails 1 assert_equal(@domain.purge_date.to_date, @domain.force_delete_date.to_date) assert_equal(@domain.outzone_date.to_date, @domain.force_delete_start.to_date + Setting.expire_warning_period.days) @@ -226,8 +227,10 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload + + assert_emails 1 assert_includes(@domain.statuses, asserted_status) end @@ -241,7 +244,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-07-06') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) @@ -256,7 +259,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-25') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_includes(@domain.statuses, asserted_status) @@ -272,7 +275,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-06') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) From a3e6bc4cf90c319a797a3b19cf6188d8360628ff Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:31:26 +0500 Subject: [PATCH 153/172] Mode DomainDeleteConfirm job to interactor --- .../send_request.rb | 22 +++++++++++++++++++ app/jobs/domain_delete_confirm_email_job.rb | 18 +-------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 app/interactions/domain_delete_confirm_interaction/send_request.rb diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb new file mode 100644 index 000000000..6e734d40d --- /dev/null +++ b/app/interactions/domain_delete_confirm_interaction/send_request.rb @@ -0,0 +1,22 @@ +module DomainDeleteConfirmInteraction + class SendRequest < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to send delete confirmation' + + def execute + log + DomainDeleteMailer.confirmation_request(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_now + end + + private + + def log + message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ + " to #{domain.registrant.email}" + Rails.logger.info(message) + end + end +end diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb index ea5a9bf49..ef9a1ba96 100644 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ b/app/jobs/domain_delete_confirm_email_job.rb @@ -1,22 +1,6 @@ class DomainDeleteConfirmEmailJob < Que::Job def run(domain_id) domain = Domain.find(domain_id) - - log(domain) - DomainDeleteMailer.confirmation_request(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_now - end - - private - - def log(domain) - message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ - " to #{domain.registrant.email}" - logger.info(message) - end - - def logger - Rails.logger + DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) end end From cb3cf37331a090d6ab03ef82afdf3bfa228e4568 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:57:28 +0500 Subject: [PATCH 154/172] Change email sending from job to delayed send --- .../send_request.rb | 2 +- app/jobs/domain_delete_confirm_email_job.rb | 8 ++++---- app/models/domain.rb | 2 +- test/integration/epp/domain/delete/base_test.rb | 13 +++++++++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb index 6e734d40d..bbf4c8d9e 100644 --- a/app/interactions/domain_delete_confirm_interaction/send_request.rb +++ b/app/interactions/domain_delete_confirm_interaction/send_request.rb @@ -8,7 +8,7 @@ module DomainDeleteConfirmInteraction log DomainDeleteMailer.confirmation_request(domain: domain, registrar: domain.registrar, - registrant: domain.registrant).deliver_now + registrant: domain.registrant).deliver_later end private diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb index ef9a1ba96..d2c814bd8 100644 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ b/app/jobs/domain_delete_confirm_email_job.rb @@ -1,6 +1,6 @@ class DomainDeleteConfirmEmailJob < Que::Job - def run(domain_id) - domain = Domain.find(domain_id) - DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) - end + # def run(domain_id) + # domain = Domain.find(domain_id) + # DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) + # end end diff --git a/app/models/domain.rb b/app/models/domain.rb index b15bb7c55..1ef66ffab 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -418,7 +418,7 @@ class Domain < ApplicationRecord pending_delete_confirmation! save(validate: false) # should check if this did succeed - DomainDeleteConfirmEmailJob.enqueue(id) + DomainDeleteConfirmInteraction::SendRequest.run(domain: self) end def cancel_pending_delete diff --git a/test/integration/epp/domain/delete/base_test.rb b/test/integration/epp/domain/delete/base_test.rb index bfdfa9f75..56a3cc31e 100644 --- a/test/integration/epp/domain/delete/base_test.rb +++ b/test/integration/epp/domain/delete/base_test.rb @@ -35,7 +35,6 @@ class EppDomainDeleteBaseTest < EppTestCase XML post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } - # binding.pry assert_includes Domain.find_by(name: 'invalid.test').statuses, DomainStatus::PENDING_DELETE_CONFIRMATION assert_epp_response :completed_successfully_action_pending end @@ -90,7 +89,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert @domain.registrant_verification_asked? @@ -121,7 +122,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert_not @domain.registrant_verification_asked? @@ -152,7 +155,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert_not @domain.registrant_verification_asked? From 5a82d7c2c01cacdc91d30ec6ff7f62cacaf10809 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:36:30 +0500 Subject: [PATCH 155/172] Fix error handling in processing request IP --- app/models/white_ip.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index c2fb51cd1..a5041095e 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,10 +41,15 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4 + ipv6).pluck(:id).flatten.uniq + new_ip4 = IPAddr.new(ip, Socket::AF_INET) + new_ip6 = IPAddr.new(ip, Socket::AF_INET6) + + result = self.all.select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === new_ip4 || + IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === new_ip6 } + ids = result.pluck(:id).flatten.uniq where(id: ids).any? + rescue IPAddr::InvalidAddressError => _e + false end # rubocop:enable Style/CaseEquality end From bace495f575a73a0b55e2542853c31f8f746b707 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 17:24:52 +0500 Subject: [PATCH 156/172] Revert "Fix error handling in processing request IP" This reverts commit 5a82d7c2c01cacdc91d30ec6ff7f62cacaf10809. --- app/models/white_ip.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index a5041095e..c2fb51cd1 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,15 +41,10 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - new_ip4 = IPAddr.new(ip, Socket::AF_INET) - new_ip6 = IPAddr.new(ip, Socket::AF_INET6) - - result = self.all.select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === new_ip4 || - IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === new_ip6 } - ids = result.pluck(:id).flatten.uniq + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4 + ipv6).pluck(:id).flatten.uniq where(id: ids).any? - rescue IPAddr::InvalidAddressError => _e - false end # rubocop:enable Style/CaseEquality end From f01e59d95aaa965664216a9bda4346894cd205d9 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 17:29:59 +0500 Subject: [PATCH 157/172] Some white ip check fixes --- app/models/white_ip.rb | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index c2fb51cd1..7198c225f 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,11 +41,35 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4 + ipv6).pluck(:id).flatten.uniq - where(id: ids).any? + Rails.logger.info "Checking if whitelist includes ip:#{ip}" + return false if ip.blank? + + where(id: ids_including(ip)).any? + end + + def ids_including(ip) + ipv4 = ipv6 = [] + if check_ip4(ip).present? + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } + end + if check_ip6(ip).present? + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6 } + end + (ipv4 + ipv6).pluck(:id).flatten.uniq end # rubocop:enable Style/CaseEquality + + def check_ip4(ip) + IPAddr.new(ip, Socket::AF_INET) + rescue StandardError => _e + nil + end + + def check_ip6(ip) + IPAddr.new(ip, Socket::AF_INET6) + rescue StandardError => _e + nil + end + end end From 3943628716d97f4d043c3ffb35dece36b903fabe Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 18:07:01 +0500 Subject: [PATCH 158/172] Add some debug messages --- app/models/authorization/restricted_ip.rb | 2 ++ app/models/registrar.rb | 3 ++- app/models/white_ip.rb | 13 +++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/authorization/restricted_ip.rb b/app/models/authorization/restricted_ip.rb index b3c7b7cdb..0149814d5 100644 --- a/app/models/authorization/restricted_ip.rb +++ b/app/models/authorization/restricted_ip.rb @@ -14,7 +14,9 @@ module Authorization end def can_access_registrar_area_sign_in_page? + Rails.logger.info "Checking if Auth::RestrictedIp.enabled: #{self.class.enabled?}" return true unless self.class.enabled? + Rails.logger.info "Checking if registrar area accessible, result: #{WhiteIp.registrar_area.include_ip?(ip)}" WhiteIp.registrar_area.include_ip?(ip) end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 040c7886b..4503bedd0 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -137,7 +137,8 @@ class Registrar < ApplicationRecord def api_ip_white?(ip) return true unless Setting.api_ip_whitelist_enabled - white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + # white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + white_ips.api.include_ip?(ip) end # Audit log is needed, therefore no raw SQL diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 7198c225f..1d3379de5 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,21 +41,26 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - Rails.logger.info "Checking if whitelist includes ip:#{ip}" + Rails.logger.info "Checking if whitelist includes ip: #{ip}" return false if ip.blank? - where(id: ids_including(ip)).any? + result = where(id: ids_including(ip)).any? + Rails.logger.info "Result is: #{result}" + result end def ids_including(ip) + Rails.logger.info "Checking ip #{ip}" ipv4 = ipv6 = [] if check_ip4(ip).present? ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } end if check_ip6(ip).present? - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6 } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) } end - (ipv4 + ipv6).pluck(:id).flatten.uniq + result = (ipv4 + ipv6).pluck(:id).flatten.uniq + Rails.logger.info "Result is #{result}" + result end # rubocop:enable Style/CaseEquality From 708e87f5a7946488bac621b5f4381fbefc5d1322 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 24 Nov 2020 13:19:08 +0500 Subject: [PATCH 159/172] CC fixes --- app/models/authorization/restricted_ip.rb | 2 -- app/models/registrar.rb | 2 +- app/models/white_ip.rb | 13 ++++--------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/app/models/authorization/restricted_ip.rb b/app/models/authorization/restricted_ip.rb index 0149814d5..b3c7b7cdb 100644 --- a/app/models/authorization/restricted_ip.rb +++ b/app/models/authorization/restricted_ip.rb @@ -14,9 +14,7 @@ module Authorization end def can_access_registrar_area_sign_in_page? - Rails.logger.info "Checking if Auth::RestrictedIp.enabled: #{self.class.enabled?}" return true unless self.class.enabled? - Rails.logger.info "Checking if registrar area accessible, result: #{WhiteIp.registrar_area.include_ip?(ip)}" WhiteIp.registrar_area.include_ip?(ip) end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 4503bedd0..149bb7541 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -137,7 +137,7 @@ class Registrar < ApplicationRecord def api_ip_white?(ip) return true unless Setting.api_ip_whitelist_enabled - # white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + white_ips.api.include_ip?(ip) end diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 1d3379de5..417633b12 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -40,17 +40,14 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality + # rubocop:disable Metrics/AbcSize def include_ip?(ip) - Rails.logger.info "Checking if whitelist includes ip: #{ip}" return false if ip.blank? - result = where(id: ids_including(ip)).any? - Rails.logger.info "Result is: #{result}" - result + where(id: ids_including(ip)).any? end def ids_including(ip) - Rails.logger.info "Checking ip #{ip}" ipv4 = ipv6 = [] if check_ip4(ip).present? ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } @@ -58,11 +55,10 @@ class WhiteIp < ApplicationRecord if check_ip6(ip).present? ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) } end - result = (ipv4 + ipv6).pluck(:id).flatten.uniq - Rails.logger.info "Result is #{result}" - result + (ipv4 + ipv6).pluck(:id).flatten.uniq end # rubocop:enable Style/CaseEquality + # rubocop:enable Metrics/AbcSize def check_ip4(ip) IPAddr.new(ip, Socket::AF_INET) @@ -75,6 +71,5 @@ class WhiteIp < ApplicationRecord rescue StandardError => _e nil end - end end From 89c5413fc702c8399474ff18f6f245af531deeba Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 24 Nov 2020 13:26:38 +0500 Subject: [PATCH 160/172] Unused class clean-up --- app/jobs/domain_delete_confirm_email_job.rb | 6 ------ test/models/whois/record_test.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 app/jobs/domain_delete_confirm_email_job.rb diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb deleted file mode 100644 index d2c814bd8..000000000 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ /dev/null @@ -1,6 +0,0 @@ -class DomainDeleteConfirmEmailJob < Que::Job - # def run(domain_id) - # domain = Domain.find(domain_id) - # DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) - # end -end diff --git a/test/models/whois/record_test.rb b/test/models/whois/record_test.rb index e900a4965..d06b23cae 100644 --- a/test/models/whois/record_test.rb +++ b/test/models/whois/record_test.rb @@ -70,6 +70,6 @@ class Whois::RecordTest < ActiveSupport::TestCase end def registration_deadline - Time.zone.now + 10.days + @registration_deadline ||= Time.zone.now + 10.days end end From 283bf8cd1adaf6d8a6ed13aac1c980d210769159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 24 Nov 2020 14:25:52 +0200 Subject: [PATCH 161/172] Fix contact version view --- app/helpers/object_versions_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index dccf03017..be8ef1217 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -3,7 +3,7 @@ module ObjectVersionsHelper version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, event_value(event, value)) + new_object.public_send(method_name, event_value(version, value)) end end end From ee92807139cc5f5c0c5ee20531a5ab84b41991c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 24 Nov 2020 15:03:06 +0200 Subject: [PATCH 162/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28810c8d8..22b3d7d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +24.11.2020 +* Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) + 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) * Refactored forceDelete cancellation for interactors [#1743](https://github.com/internetee/registry/issues/1743) From b39de0885a4dd88ce95d111da6196705df2db252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 24 Nov 2020 15:29:50 +0200 Subject: [PATCH 163/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b3d7d02..ebbc3d905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 24.11.2020 * Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) +* Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580) 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) From 09a58fa432193dee065ebcbc0556cc96e877e0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 27 Nov 2020 18:12:49 +0200 Subject: [PATCH 164/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbc3d905..44329c4d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +27.11.2020 +* Refactored delete confirmation for interactors [#1753](https://github.com/internetee/registry/issues/1753) + 24.11.2020 * Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) * Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580) From 5363c546a598ecfe8460a43c6bb2c105ea764ab0 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 13:46:53 +0500 Subject: [PATCH 165/172] Move all the existing interactors to Domains namespace --- .../cancel_force_delete_interaction/base.rb | 7 ---- .../cancel_force_delete.rb | 10 ----- .../clear_force_delete_data.rb | 10 ----- .../notify_registrar.rb | 8 ---- .../remove_force_delete_statuses.rb | 11 ----- .../restore_statuses_before_force_delete.rb | 9 ----- .../send_request.rb | 22 ---------- .../domains/cancel_force_delete/base.rb | 9 +++++ .../cancel_force_delete.rb | 12 ++++++ .../clear_force_delete_data.rb | 12 ++++++ .../cancel_force_delete/notify_registrar.rb | 10 +++++ .../remove_force_delete_statuses.rb | 13 ++++++ .../restore_statuses_before_force_delete.rb | 11 +++++ .../domains/delete_confirm/send_request.rb | 24 +++++++++++ app/interactions/domains/force_delete/base.rb | 17 ++++++++ .../domains/force_delete/check_discarded.rb | 12 ++++++ .../domains/force_delete/notify_by_email.rb | 23 +++++++++++ .../domains/force_delete/notify_registrar.rb | 12 ++++++ .../domains/force_delete/post_set_process.rb | 19 +++++++++ .../domains/force_delete/prepare_domain.rb | 15 +++++++ .../domains/force_delete/set_force_delete.rb | 14 +++++++ .../domains/force_delete/set_status.rb | 40 +++++++++++++++++++ .../force_delete_interaction/base.rb | 16 -------- .../check_discarded.rb | 11 ----- .../notify_by_email.rb | 21 ---------- .../notify_registrar.rb | 10 ----- .../post_set_process.rb | 17 -------- .../prepare_domain.rb | 13 ------ .../set_force_delete.rb | 12 ------ .../force_delete_interaction/set_status.rb | 38 ------------------ app/models/concerns/domain/force_delete.rb | 8 ++-- app/models/domain.rb | 2 +- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 34 files changed, 250 insertions(+), 222 deletions(-) delete mode 100644 app/interactions/cancel_force_delete_interaction/base.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb delete mode 100644 app/interactions/domain_delete_confirm_interaction/send_request.rb create mode 100644 app/interactions/domains/cancel_force_delete/base.rb create mode 100644 app/interactions/domains/cancel_force_delete/cancel_force_delete.rb create mode 100644 app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb create mode 100644 app/interactions/domains/cancel_force_delete/notify_registrar.rb create mode 100644 app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb create mode 100644 app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb create mode 100644 app/interactions/domains/delete_confirm/send_request.rb create mode 100644 app/interactions/domains/force_delete/base.rb create mode 100644 app/interactions/domains/force_delete/check_discarded.rb create mode 100644 app/interactions/domains/force_delete/notify_by_email.rb create mode 100644 app/interactions/domains/force_delete/notify_registrar.rb create mode 100644 app/interactions/domains/force_delete/post_set_process.rb create mode 100644 app/interactions/domains/force_delete/prepare_domain.rb create mode 100644 app/interactions/domains/force_delete/set_force_delete.rb create mode 100644 app/interactions/domains/force_delete/set_status.rb delete mode 100644 app/interactions/force_delete_interaction/base.rb delete mode 100644 app/interactions/force_delete_interaction/check_discarded.rb delete mode 100644 app/interactions/force_delete_interaction/notify_by_email.rb delete mode 100644 app/interactions/force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/force_delete_interaction/post_set_process.rb delete mode 100644 app/interactions/force_delete_interaction/prepare_domain.rb delete mode 100644 app/interactions/force_delete_interaction/set_force_delete.rb delete mode 100644 app/interactions/force_delete_interaction/set_status.rb diff --git a/app/interactions/cancel_force_delete_interaction/base.rb b/app/interactions/cancel_force_delete_interaction/base.rb deleted file mode 100644 index 595279d87..000000000 --- a/app/interactions/cancel_force_delete_interaction/base.rb +++ /dev/null @@ -1,7 +0,0 @@ -module CancelForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to cancel ForceDelete on' - end -end diff --git a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb deleted file mode 100644 index 2f45bf0e4..000000000 --- a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb +++ /dev/null @@ -1,10 +0,0 @@ -module CancelForceDeleteInteraction - class CancelForceDelete < Base - def execute - compose(RemoveForceDeleteStatuses, inputs) - compose(RestoreStatusesBeforeForceDelete, inputs) - compose(ClearForceDeleteData, inputs) - compose(NotifyRegistrar, inputs) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb deleted file mode 100644 index ccc0714f7..000000000 --- a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb +++ /dev/null @@ -1,10 +0,0 @@ -module CancelForceDeleteInteraction - class ClearForceDeleteData < Base - def execute - domain.force_delete_data = nil - domain.force_delete_date = nil - domain.force_delete_start = nil - domain.save(validate: false) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb deleted file mode 100644 index 3ded4c489..000000000 --- a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,8 +0,0 @@ -module CancelForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', - domain_name: domain.name)) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb deleted file mode 100644 index 1cc4989e5..000000000 --- a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb +++ /dev/null @@ -1,11 +0,0 @@ -module CancelForceDeleteInteraction - class RemoveForceDeleteStatuses < Base - def execute - domain.statuses.delete(DomainStatus::FORCE_DELETE) - domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) - domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) - domain.statuses.delete(DomainStatus::CLIENT_HOLD) - domain.save(validate: false) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb deleted file mode 100644 index 06b6bbd18..000000000 --- a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb +++ /dev/null @@ -1,9 +0,0 @@ -module CancelForceDeleteInteraction - class RestoreStatusesBeforeForceDelete < Base - def execute - domain.statuses = domain.statuses_before_force_delete - domain.statuses_before_force_delete = nil - domain.save(validate: false) - end - end -end diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb deleted file mode 100644 index bbf4c8d9e..000000000 --- a/app/interactions/domain_delete_confirm_interaction/send_request.rb +++ /dev/null @@ -1,22 +0,0 @@ -module DomainDeleteConfirmInteraction - class SendRequest < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to send delete confirmation' - - def execute - log - DomainDeleteMailer.confirmation_request(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_later - end - - private - - def log - message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ - " to #{domain.registrant.email}" - Rails.logger.info(message) - end - end -end diff --git a/app/interactions/domains/cancel_force_delete/base.rb b/app/interactions/domains/cancel_force_delete/base.rb new file mode 100644 index 000000000..b15a3ae77 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/base.rb @@ -0,0 +1,9 @@ +module Domains + module CancelForceDelete + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to cancel ForceDelete on' + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb b/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb new file mode 100644 index 000000000..7c4ca90e1 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb @@ -0,0 +1,12 @@ +module Domains + module CancelForceDelete + class CancelForceDelete < Base + def execute + compose(RemoveForceDeleteStatuses, inputs) + compose(RestoreStatusesBeforeForceDelete, inputs) + compose(ClearForceDeleteData, inputs) + compose(NotifyRegistrar, inputs) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb b/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb new file mode 100644 index 000000000..066df04fd --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb @@ -0,0 +1,12 @@ +module Domains + module CancelForceDelete + class ClearForceDeleteData < Base + def execute + domain.force_delete_data = nil + domain.force_delete_date = nil + domain.force_delete_start = nil + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/notify_registrar.rb b/app/interactions/domains/cancel_force_delete/notify_registrar.rb new file mode 100644 index 000000000..0de69e13e --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/notify_registrar.rb @@ -0,0 +1,10 @@ +module Domains + module CancelForceDelete + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', + domain_name: domain.name)) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb b/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb new file mode 100644 index 000000000..c77820ecf --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb @@ -0,0 +1,13 @@ +module Domains + module CancelForceDelete + class RemoveForceDeleteStatuses < Base + def execute + domain.statuses.delete(DomainStatus::FORCE_DELETE) + domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) + domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) + domain.statuses.delete(DomainStatus::CLIENT_HOLD) + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb new file mode 100644 index 000000000..c55c06764 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb @@ -0,0 +1,11 @@ +module Domains + module CancelForceDelete + class RestoreStatusesBeforeForceDelete < Base + def execute + domain.statuses = domain.statuses_before_force_delete + domain.statuses_before_force_delete = nil + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/delete_confirm/send_request.rb b/app/interactions/domains/delete_confirm/send_request.rb new file mode 100644 index 000000000..91afaefb8 --- /dev/null +++ b/app/interactions/domains/delete_confirm/send_request.rb @@ -0,0 +1,24 @@ +module Domains + module DeleteConfirm + class SendRequest < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to send delete confirmation' + + def execute + log + DomainDeleteMailer.confirmation_request(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_later + end + + private + + def log + message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})"\ + " to #{domain.registrant.email}" + Rails.logger.info(message) + end + end + end +end diff --git a/app/interactions/domains/force_delete/base.rb b/app/interactions/domains/force_delete/base.rb new file mode 100644 index 000000000..27601c1d2 --- /dev/null +++ b/app/interactions/domains/force_delete/base.rb @@ -0,0 +1,17 @@ +module Domains + module ForceDelete + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end + end +end diff --git a/app/interactions/domains/force_delete/check_discarded.rb b/app/interactions/domains/force_delete/check_discarded.rb new file mode 100644 index 000000000..cbb9c1dc6 --- /dev/null +++ b/app/interactions/domains/force_delete/check_discarded.rb @@ -0,0 +1,12 @@ +module Domains + module ForceDelete + class CheckDiscarded < Base + def execute + return true unless domain.discarded? + + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + errors.add(:domain, message) + end + end + end +end diff --git a/app/interactions/domains/force_delete/notify_by_email.rb b/app/interactions/domains/force_delete/notify_by_email.rb new file mode 100644 index 000000000..b60f54a5e --- /dev/null +++ b/app/interactions/domains/force_delete/notify_by_email.rb @@ -0,0 +1,23 @@ +module Domains + module ForceDelete + class NotifyByEmail < Base + def execute + return unless notify_by_email + + if type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: domain.notification_template) + end + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactions/domains/force_delete/notify_registrar.rb b/app/interactions/domains/force_delete/notify_registrar.rb new file mode 100644 index 000000000..522502640 --- /dev/null +++ b/app/interactions/domains/force_delete/notify_registrar.rb @@ -0,0 +1,12 @@ +module Domains + module ForceDelete + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactions/domains/force_delete/post_set_process.rb b/app/interactions/domains/force_delete/post_set_process.rb new file mode 100644 index 000000000..904149f93 --- /dev/null +++ b/app/interactions/domains/force_delete/post_set_process.rb @@ -0,0 +1,19 @@ +module Domains + module ForceDelete + class PostSetProcess < Base + def execute + statuses = domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/force_delete/prepare_domain.rb b/app/interactions/domains/force_delete/prepare_domain.rb new file mode 100644 index 000000000..74eea21ed --- /dev/null +++ b/app/interactions/domains/force_delete/prepare_domain.rb @@ -0,0 +1,15 @@ +module Domains + module ForceDelete + class PrepareDomain < Base + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + + def execute + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= STATUSES_TO_SET + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/force_delete/set_force_delete.rb b/app/interactions/domains/force_delete/set_force_delete.rb new file mode 100644 index 000000000..16a0b09fa --- /dev/null +++ b/app/interactions/domains/force_delete/set_force_delete.rb @@ -0,0 +1,14 @@ +module Domains + module ForceDelete + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end + end +end diff --git a/app/interactions/domains/force_delete/set_status.rb b/app/interactions/domains/force_delete/set_status.rb new file mode 100644 index 000000000..b0a53ad82 --- /dev/null +++ b/app/interactions/domains/force_delete/set_status.rb @@ -0,0 +1,40 @@ +module Domains + module ForceDelete + class SetStatus < Base + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + def force_delete_fast_track + domain.force_delete_date = Time.zone.today + + expire_warning_period_days + + redemption_grace_period_days + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? + end + + private + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end + end + end +end diff --git a/app/interactions/force_delete_interaction/base.rb b/app/interactions/force_delete_interaction/base.rb deleted file mode 100644 index 4764ce8fe..000000000 --- a/app/interactions/force_delete_interaction/base.rb +++ /dev/null @@ -1,16 +0,0 @@ -module ForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to set ForceDelete on' - symbol :type, - default: :fast_track, - description: 'Force delete type, might be :fast_track or :soft' - boolean :notify_by_email, - default: false, - description: 'Do we need to send email notification' - - validates :type, inclusion: { in: %i[fast_track soft] } - end -end - diff --git a/app/interactions/force_delete_interaction/check_discarded.rb b/app/interactions/force_delete_interaction/check_discarded.rb deleted file mode 100644 index e0c893294..000000000 --- a/app/interactions/force_delete_interaction/check_discarded.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ForceDeleteInteraction - class CheckDiscarded < Base - def execute - return true unless domain.discarded? - - message = 'Force delete procedure cannot be scheduled while a domain is discarded' - errors.add(:domain, message) - end - end -end - diff --git a/app/interactions/force_delete_interaction/notify_by_email.rb b/app/interactions/force_delete_interaction/notify_by_email.rb deleted file mode 100644 index 541df258d..000000000 --- a/app/interactions/force_delete_interaction/notify_by_email.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ForceDeleteInteraction - class NotifyByEmail < Base - def execute - return unless notify_by_email - - if type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - - def send_email - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - end -end diff --git a/app/interactions/force_delete_interaction/notify_registrar.rb b/app/interactions/force_delete_interaction/notify_registrar.rb deleted file mode 100644 index 691e54f7e..000000000 --- a/app/interactions/force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,10 +0,0 @@ -module ForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end -end diff --git a/app/interactions/force_delete_interaction/post_set_process.rb b/app/interactions/force_delete_interaction/post_set_process.rb deleted file mode 100644 index e51acf9b7..000000000 --- a/app/interactions/force_delete_interaction/post_set_process.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ForceDeleteInteraction - class PostSetProcess < Base - def execute - statuses = domain.statuses - # Stop all pending actions - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - - # Allow deletion - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - domain.save(validate: false) - end - end -end diff --git a/app/interactions/force_delete_interaction/prepare_domain.rb b/app/interactions/force_delete_interaction/prepare_domain.rb deleted file mode 100644 index 97f364145..000000000 --- a/app/interactions/force_delete_interaction/prepare_domain.rb +++ /dev/null @@ -1,13 +0,0 @@ -module ForceDeleteInteraction - class PrepareDomain < Base - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - - def execute - domain.statuses_before_force_delete = domain.statuses - domain.statuses |= STATUSES_TO_SET - domain.save(validate: false) - end - end -end diff --git a/app/interactions/force_delete_interaction/set_force_delete.rb b/app/interactions/force_delete_interaction/set_force_delete.rb deleted file mode 100644 index 4608b5127..000000000 --- a/app/interactions/force_delete_interaction/set_force_delete.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ForceDeleteInteraction - class SetForceDelete < Base - def execute - compose(CheckDiscarded, inputs) - compose(PrepareDomain, inputs) - compose(SetStatus, inputs) - compose(PostSetProcess, inputs) - compose(NotifyRegistrar, inputs) - compose(NotifyByEmail, inputs) - end - end -end diff --git a/app/interactions/force_delete_interaction/set_status.rb b/app/interactions/force_delete_interaction/set_status.rb deleted file mode 100644 index 7d104aeee..000000000 --- a/app/interactions/force_delete_interaction/set_status.rb +++ /dev/null @@ -1,38 +0,0 @@ -module ForceDeleteInteraction - class SetStatus < Base - def execute - domain.force_delete_type = type - type == :fast_track ? force_delete_fast_track : force_delete_soft - domain.save(validate: false) - end - - def force_delete_fast_track - domain.force_delete_date = Time.zone.today + - expire_warning_period_days + - redemption_grace_period_days - domain.force_delete_start = Time.zone.today + 1.day - end - - def force_delete_soft - years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) if years.positive? - end - - private - - def soft_forcedelete_dates(years) - domain.force_delete_start = domain.valid_to - years.years - domain.force_delete_date = domain.force_delete_start + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def redemption_grace_period_days - Setting.redemption_grace_period.days + 1.day - end - - def expire_warning_period_days - Setting.expire_warning_period.days - end - end -end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index ff869fc0a..6e0f6c886 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,13 +53,13 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - ForceDeleteInteraction::SetForceDelete.run(domain: self, - type: type, - notify_by_email: notify_by_email) + Domains::ForceDelete::SetForceDelete.run(domain: self, + type: type, + notify_by_email: notify_by_email) end def cancel_force_delete - CancelForceDeleteInteraction::CancelForceDelete.run(domain: self) + Domains::CancelForceDelete::CancelForceDelete.run(domain: self) end def outzone_date diff --git a/app/models/domain.rb b/app/models/domain.rb index 1ef66ffab..dc7d86da8 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -418,7 +418,7 @@ class Domain < ApplicationRecord pending_delete_confirmation! save(validate: false) # should check if this did succeed - DomainDeleteConfirmInteraction::SendRequest.run(domain: self) + Domains::DeleteConfirm::SendRequest.run(domain: self) end def cancel_pending_delete diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 7c205fa74..dcb9b80f6 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -113,7 +113,7 @@ class ForceDeleteTest < ActionMailer::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - result = ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) + result = Domains::ForceDelete::SetForceDelete.run(domain: @domain, type: :fast_track) assert_not result.valid? assert_not @domain.force_delete_scheduled? diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index cc88cf35f..ae12f4a1e 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) + Domains::ForceDelete::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 8ff8aa78c8e12ae6523c3cc0ff1524b12de44d81 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 16:08:32 +0500 Subject: [PATCH 166/172] Move interactor to namespace --- .../client_hold_interaction/base.rb | 8 --- .../process_client_hold.rb | 67 ------------------ .../set_client_hold.rb | 15 ---- app/interactions/domains/client_hold/base.rb | 10 +++ .../client_hold/process_client_hold.rb | 69 +++++++++++++++++++ .../domains/client_hold/set_client_hold.rb | 17 +++++ app/models/domain_cron.rb | 2 +- test/models/domain/force_delete_test.rb | 10 +-- 8 files changed, 102 insertions(+), 96 deletions(-) delete mode 100644 app/interactions/client_hold_interaction/base.rb delete mode 100644 app/interactions/client_hold_interaction/process_client_hold.rb delete mode 100644 app/interactions/client_hold_interaction/set_client_hold.rb create mode 100644 app/interactions/domains/client_hold/base.rb create mode 100644 app/interactions/domains/client_hold/process_client_hold.rb create mode 100644 app/interactions/domains/client_hold/set_client_hold.rb diff --git a/app/interactions/client_hold_interaction/base.rb b/app/interactions/client_hold_interaction/base.rb deleted file mode 100644 index be8b8b0a8..000000000 --- a/app/interactions/client_hold_interaction/base.rb +++ /dev/null @@ -1,8 +0,0 @@ -module ClientHoldInteraction - class Base < ActiveInteraction::Base - def to_stdout(message) - time = Time.zone.now.utc - STDOUT << "#{time} - #{message}\n" unless Rails.env.test? - end - end -end diff --git a/app/interactions/client_hold_interaction/process_client_hold.rb b/app/interactions/client_hold_interaction/process_client_hold.rb deleted file mode 100644 index 453c400cc..000000000 --- a/app/interactions/client_hold_interaction/process_client_hold.rb +++ /dev/null @@ -1,67 +0,0 @@ -module ClientHoldInteraction - class ProcessClientHold < Base - object :domain, - class: Domain, - description: 'Domain to set ClientHold on' - - # rubocop:disable Metrics/AbcSize - def execute - notify_on_grace_period if should_notify_on_soft_force_delete? - - return unless client_holdable? - - domain.statuses << DomainStatus::CLIENT_HOLD - to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") - - domain.save(validate: false) - notify_client_hold - - to_stdout("Successfully set client_hold on (#{domain.name})") - end - - def notify_on_grace_period - domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', - domain_name: domain.name, - date: domain.force_delete_start)) - send_mail if domain.template_name.present? - domain.update(contact_notification_sent_date: Time.zone.today) - end - - def notify_client_hold - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - - def send_mail - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.template_name).deliver_now - end - - def should_notify_on_soft_force_delete? - domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && - domain.force_delete_start.to_date <= Time.zone.now.to_date && - domain.force_delete_type.to_sym == :soft && - !domain.statuses.include?(DomainStatus::CLIENT_HOLD) - end - # rubocop:enable Metrics/AbcSize - - def client_holdable? - domain.force_delete_scheduled? && - !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && - domain.force_delete_start.present? && - force_delete_lte_today && force_delete_lte_valid_date - end - - def force_delete_lte_today - domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now - end - - def force_delete_lte_valid_date - domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to - end - end -end diff --git a/app/interactions/client_hold_interaction/set_client_hold.rb b/app/interactions/client_hold_interaction/set_client_hold.rb deleted file mode 100644 index d723d5cc8..000000000 --- a/app/interactions/client_hold_interaction/set_client_hold.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ClientHoldInteraction - class SetClientHold < Base - def execute - to_stdout('Setting client_hold to domains\n') - - ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" - - ::Domain.force_delete_scheduled.each do |domain| - ClientHoldInteraction::ProcessClientHold.run(domain: domain) - end - - to_stdout('All client_hold setting are done\n') - end - end -end diff --git a/app/interactions/domains/client_hold/base.rb b/app/interactions/domains/client_hold/base.rb new file mode 100644 index 000000000..c3c626b79 --- /dev/null +++ b/app/interactions/domains/client_hold/base.rb @@ -0,0 +1,10 @@ +module Domains + module ClientHold + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/client_hold/process_client_hold.rb b/app/interactions/domains/client_hold/process_client_hold.rb new file mode 100644 index 000000000..8b8d60403 --- /dev/null +++ b/app/interactions/domains/client_hold/process_client_hold.rb @@ -0,0 +1,69 @@ +module Domains + module ClientHold + class ProcessClientHold < Base + object :domain, + class: Domain, + description: 'Domain to set ClientHold on' + + # rubocop:disable Metrics/AbcSize + def execute + notify_on_grace_period if should_notify_on_soft_force_delete? + + return unless client_holdable? + + domain.statuses << DomainStatus::CLIENT_HOLD + to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") + + domain.save(validate: false) + notify_client_hold + + to_stdout("Successfully set client_hold on (#{domain.name})") + end + + def notify_on_grace_period + domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', + domain_name: domain.name, + date: domain.force_delete_start)) + send_mail if domain.template_name.present? + domain.update(contact_notification_sent_date: Time.zone.today) + end + + def notify_client_hold + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + + def send_mail + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.template_name).deliver_now + end + + def should_notify_on_soft_force_delete? + domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && + domain.force_delete_start.to_date <= Time.zone.now.to_date && + domain.force_delete_type.to_sym == :soft && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) + end + # rubocop:enable Metrics/AbcSize + + def client_holdable? + domain.force_delete_scheduled? && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && + domain.force_delete_start.present? && + force_delete_lte_today && force_delete_lte_valid_date + end + + def force_delete_lte_today + domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now + end + + def force_delete_lte_valid_date + domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to + end + end + end +end diff --git a/app/interactions/domains/client_hold/set_client_hold.rb b/app/interactions/domains/client_hold/set_client_hold.rb new file mode 100644 index 000000000..0e54b531f --- /dev/null +++ b/app/interactions/domains/client_hold/set_client_hold.rb @@ -0,0 +1,17 @@ +module Domains + module ClientHold + class SetClientHold < Base + def execute + to_stdout('Setting client_hold to domains\n') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + ::Domain.force_delete_scheduled.each do |domain| + Domains::ClientHold::ProcessClientHold.run(domain: domain) + end + + to_stdout('All client_hold setting are done\n') + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index 613762062..d09141d52 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -79,6 +79,6 @@ class DomainCron end def self.start_client_hold - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index d9e7a3632..304a7a18b 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -206,7 +206,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_emails 1 @@ -227,7 +227,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_emails 1 @@ -244,7 +244,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-07-06') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) @@ -259,7 +259,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-25') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_includes(@domain.statuses, asserted_status) @@ -275,7 +275,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-06') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) From cb7ff317e42a8a1dd2105834301e065e0ab676f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Mon, 30 Nov 2020 14:26:46 +0200 Subject: [PATCH 167/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44329c4d0..f418d4034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +30.11.2020 +* Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762) + 27.11.2020 * Refactored delete confirmation for interactors [#1753](https://github.com/internetee/registry/issues/1753) From dc5bbb587f1490eb714742ff7db3acefc30a8c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 16:52:28 +0200 Subject: [PATCH 168/172] EPP: Show error when trying to remove unassigned domain status --- app/models/epp/domain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index c46732712..a5922f48c 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -417,7 +417,7 @@ class Epp::Domain < Domain if statuses.include?(x) to_destroy << x else - add_epp_error('2303', 'status', x, [:domain_statuses, :not_found]) + add_epp_error('2303', 'status', x, [:statuses, :not_found]) end end @@ -432,7 +432,7 @@ class Epp::Domain < Domain frame.css('status').each do |x| unless DomainStatus::CLIENT_STATUSES.include?(x['s']) - add_epp_error('2303', 'status', x['s'], [:domain_statuses, :not_found]) + add_epp_error('2303', 'status', x['s'], [:statuses, :not_found]) next end From 490de2ee4bd89b83df277e8848efbdbe479edbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 17:05:21 +0200 Subject: [PATCH 169/172] Add test for unassigned domain status removal --- .../epp/domain/update/base_test.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index 6ce455948..14e806fca 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -503,6 +503,30 @@ class EppDomainUpdateBaseTest < EppTestCase assert_not_includes(@domain.statuses, DomainStatus::CLIENT_HOLD) end + def test_update_domain_returns_error_when_removing_unassigned_status + assert_not_includes(@domain.statuses, DomainStatus::CLIENT_HOLD) + request_xml = <<-XML + + + + + + #{@domain.name} + + + + + + + + XML + + post epp_update_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + @domain.reload + assert_epp_response :object_does_not_exist + end + private def assert_verification_and_notification_emails From e5a2a6cf0f33281c8f16874f5e3343bcf635465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 17:10:04 +0200 Subject: [PATCH 170/172] Fix CC issues --- app/models/epp/domain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index a5922f48c..7fb23a6e9 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -417,7 +417,7 @@ class Epp::Domain < Domain if statuses.include?(x) to_destroy << x else - add_epp_error('2303', 'status', x, [:statuses, :not_found]) + add_epp_error('2303', 'status', x, %i[statuses not_found]) end end @@ -432,7 +432,7 @@ class Epp::Domain < Domain frame.css('status').each do |x| unless DomainStatus::CLIENT_STATUSES.include?(x['s']) - add_epp_error('2303', 'status', x['s'], [:statuses, :not_found]) + add_epp_error('2303', 'status', x['s'], %i[statuses not_found]) next end From 512b181cd785e79d087f3d491d13ad6ad4787db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 1 Dec 2020 10:00:04 +0200 Subject: [PATCH 171/172] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f418d4034..489ba94d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +01.12.2020 +* Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) + 30.11.2020 * Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762) From 8916cb29d1f631f690371abe24f6d80d50efd071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 1 Dec 2020 15:48:13 +0200 Subject: [PATCH 172/172] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 489ba94d3..9774d5cb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 01.12.2020 * Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) +* Fixed internal error on removing clientHold status when not present [#1766](https://github.com/internetee/registry/issues/1766) 30.11.2020 * Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762)