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 01/69] 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 02/69] 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 03/69] 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 04/69] 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 05/69] 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 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 06/69] 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 07/69] 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 08/69] 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 09/69] 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 10/69] 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 11/69] 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 12/69] 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 13/69] 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 14/69] 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 15/69] 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 16/69] 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 17/69] 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 18/69] 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 19/69] 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 20/69] 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 21/69] 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 22/69] 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 23/69] 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 24/69] 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 25/69] 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 26/69] 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 27/69] 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 28/69] 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 29/69] 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 30/69] 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 31/69] 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 32/69] 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 33/69] 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 34/69] 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 35/69] 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 36/69] 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 37/69] 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 38/69] 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 39/69] 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 40/69] 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 41/69] 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 42/69] 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 43/69] 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 44/69] 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 45/69] 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 46/69] 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 47/69] 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 48/69] 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 49/69] 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 50/69] 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 51/69] 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 52/69] 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 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 53/69] 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 54/69] 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 55/69] 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 56/69] 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 57/69] 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 58/69] 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 59/69] 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 60/69] 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 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 61/69] 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 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 62/69] 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 63/69] 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 64/69] 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 65/69] 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 66/69] 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 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 67/69] 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 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 68/69] 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 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 69/69] 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