diff --git a/app/helpers/epp/domains_helper.rb b/app/helpers/epp/domains_helper.rb index d61dafae3..b8e678a2e 100644 --- a/app/helpers/epp/domains_helper.rb +++ b/app/helpers/epp/domains_helper.rb @@ -64,15 +64,37 @@ module Epp::DomainsHelper def transfer_domain @domain = find_domain(secure: false) handle_errors(@domain) and return unless @domain + handle_errors(@domain) and return unless @domain.authenticate(domain_transfer_params[:pw]) - @domain_transfer = @domain.transfer(domain_transfer_params) - if @domain_transfer - @domain.attach_legal_document(Epp::EppDomain.parse_legal_document_from_frame(parsed_frame)) - @domain.save + if domain_transfer_params[:action] == 'query' + + if @domain.pending_transfer + @domain_transfer = @domain.pending_transfer + else + @domain_transfer = @domain.query_transfer(domain_transfer_params, parsed_frame) + handle_errors(@domain) and return unless @domain_transfer + end + + elsif domain_transfer_params[:action] == 'approve' + + if @domain.pending_transfer + @domain_transfer = @domain.approve_transfer(domain_transfer_params, parsed_frame) + handle_errors(@domain) and return unless @domain_transfer + else + epp_errors << { code: '2303', msg: I18n.t('pending_transfer_was_not_found') } + handle_errors(@domain) and return + end + + elsif domain_transfer_params[:action] == 'reject' + if @domain.pending_transfer + @domain_transfer = @domain.reject_transfer(domain_transfer_params, parsed_frame) + handle_errors(@domain) and return unless @domain_transfer + else + epp_errors << { code: '2303', msg: I18n.t('pending_transfer_was_not_found') } + handle_errors(@domain) and return + end end - handle_errors(@domain) and return unless @domain_transfer - render '/epp/domains/transfer' end @@ -167,7 +189,7 @@ module Epp::DomainsHelper return false unless attrs_present op = parsed_frame.css('transfer').first[:op] - return true if %w(approve query).include?(op) + return true if %w(approve query reject).include?(op) epp_errors << { code: '2306', msg: I18n.t('errors.messages.attribute_op_is_invalid') } false end diff --git a/app/models/epp/epp_domain.rb b/app/models/epp/epp_domain.rb index 02718ce97..7d077e248 100644 --- a/app/models/epp/epp_domain.rb +++ b/app/models/epp/epp_domain.rb @@ -290,66 +290,104 @@ class Epp::EppDomain < Domain ### TRANSFER ### - # rubocop: disable Metrics/PerceivedComplexity # rubocop: disable Metrics/MethodLength - # rubocop: disable Metrics/CyclomaticComplexity - def transfer(params) - return false unless authenticate(params[:pw]) + def query_transfer(params, parsed_frame) + return false unless can_be_transferred_to?(params[:current_user].registrar) - pt = pending_transfer - if pt && params[:action] == 'approve' - if approve_pending_transfer(params[:current_user]) - return pt.reload - else - return false + transaction do + begin + if Setting.transfer_wait_time > 0 + dt = domain_transfers.create!( + status: DomainTransfer::PENDING, + transfer_requested_at: Time.zone.now, + transfer_to: params[:current_user].registrar, + transfer_from: registrar + ) + + registrar.messages.create!( + body: I18n.t('transfer_requested'), + attached_obj_id: dt.id, + attached_obj_type: dt.class.to_s + ) + + else + dt = domain_transfers.create!( + status: DomainTransfer::SERVER_APPROVED, + transfer_requested_at: Time.zone.now, + transferred_at: Time.zone.now, + transfer_to: params[:current_user].registrar, + transfer_from: registrar + ) + + generate_auth_info + + self.registrar = params[:current_user].registrar + end + + attach_legal_document(self.class.parse_legal_document_from_frame(parsed_frame)) + save!(validate: false) + + return dt + rescue => _e + add_epp_error('2306', nil, nil, I18n.t('action_failed_due_to_internal_error')) + raise ActiveRecord::Rollback end - end + end + # rubocop: enable Metrics/MethodLength - if !pt && params[:action] != 'query' - add_epp_error('2306', nil, nil, I18n.t('errors.messages.attribute_op_is_invalid')) + def approve_transfer(params, parsed_frame) + pt = pending_transfer + if params[:current_user].registrar != pt.transfer_from + add_epp_error('2304', nil, nil, I18n.t('transfer_can_be_approved_only_by_current_registrar')) return false end - if !pt && params[:action] == 'query' - return false unless can_be_transferred_to?(params[:current_user].registrar) + transaction do + begin + pt.update!( + status: DomainTransfer::CLIENT_APPROVED, + transferred_at: Time.zone.now + ) + + generate_auth_info + + self.registrar = pt.transfer_to + + attach_legal_document(self.class.parse_legal_document_from_frame(parsed_frame)) + save!(validate: false) + rescue => _e + add_epp_error('2306', nil, nil, I18n.t('action_failed_due_to_internal_error')) + raise ActiveRecord::Rollback + end end - return pt if pt - if Setting.transfer_wait_time > 0 - dt = domain_transfers.create( - status: DomainTransfer::PENDING, - transfer_requested_at: Time.zone.now, - transfer_to: params[:current_user].registrar, - transfer_from: registrar - ) - - registrar.messages.create( - body: I18n.t('transfer_requested'), - attached_obj_id: dt.id, - attached_obj_type: dt.class.to_s - ) - - else - dt = domain_transfers.create( - status: DomainTransfer::SERVER_APPROVED, - transfer_requested_at: Time.zone.now, - transferred_at: Time.zone.now, - transfer_to: params[:current_user].registrar, - transfer_from: registrar - ) - - generate_auth_info - - self.registrar = params[:current_user].registrar - save(validate: false) - end - - dt + pt + end + + def reject_transfer(params, parsed_frame) + pt = pending_transfer + if params[:current_user].registrar != pt.transfer_from + add_epp_error('2304', nil, nil, I18n.t('transfer_can_be_rejected_only_by_current_registrar')) + return false + end + + transaction do + begin + pt.update!( + status: DomainTransfer::CLIENT_REJECTED + ) + + attach_legal_document(self.class.parse_legal_document_from_frame(parsed_frame)) + save!(validate: false) + rescue => _e + add_epp_error('2306', nil, nil, I18n.t('action_failed_due_to_internal_error')) + raise ActiveRecord::Rollback + end + end + + pt end - # rubocop: enable Metrics/PerceivedComplexity - # rubocop: enable Metrics/MethodLength - # rubocop: enable Metrics/CyclomaticComplexity def approve_pending_transfer(current_user) pt = pending_transfer diff --git a/config/locales/en.yml b/config/locales/en.yml index eda413bef..bbd95ba1d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -478,3 +478,6 @@ en: admin_contacts_max_count: 'Admin contacts maximum count' tech_contacts_min_count: 'Tech contacts minimum count' tech_contacts_max_count: 'Tech contacts maximum count' + action_failed_due_to_internal_error: 'Action failed due to internal error' + pending_transfer_was_not_found: 'Pending transfer was not found' + transfer_can_be_rejected_only_by_current_registrar: 'Transfer can be rejected only by current registrar' diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index e714d17bd..64c3a6fda 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -218,6 +218,74 @@ describe 'EPP Domain', epp: true do expect(response[:result_code]).to eq('2201') expect(response[:msg]).to eq('Authorization error') end + + it 'creates a domain transfer with legal document' do + expect(domain.legal_documents.count).to eq(0) + pw = domain.auth_info + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }, 'query', { + _anonymus: [ + legalDocument: { + value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', + attrs: { type: 'pdf' } + } + ] + }) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:result_code]).to eq('1000') + expect(domain.legal_documents.count).to eq(1) + end + + it 'creates a domain transfer with legal document' do + Setting.transfer_wait_time = 1 + expect(domain.legal_documents.count).to eq(0) + pw = domain.auth_info + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }, 'query', { + _anonymus: [ + legalDocument: { + value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', + attrs: { type: 'pdf' } + } + ] + }) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:result_code]).to eq('1000') + expect(domain.legal_documents.count).to eq(1) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:result_code]).to eq('1000') + expect(domain.legal_documents.count).to eq(1) # does not add another legal document + end + + it 'rejects a domain transfer' do + domain.domain_transfers.create({ + status: DomainTransfer::PENDING, + transfer_requested_at: Time.zone.now, + transfer_to: elkdata, + transfer_from: zone + }) + + pw = domain.auth_info + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }, 'reject', { + _anonymus: [ + legalDocument: { + value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', + attrs: { type: 'pdf' } + } + ] + }) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:result_code]).to eq('2304') + expect(response[:msg]).to eq('Transfer can be rejected only by current registrar') + expect(domain.legal_documents.count).to eq(0) + + response = epp_request(xml, :xml, :zone) + expect(response[:result_code]).to eq('1000') + expect(domain.pending_transfer).to be_nil + expect(domain.legal_documents.count).to eq(1) + end end context 'with citizen as an owner' do diff --git a/spec/support/epp.rb b/spec/support/epp.rb index fbede7e5a..6203c2efa 100644 --- a/spec/support/epp.rb +++ b/spec/support/epp.rb @@ -279,7 +279,7 @@ module Epp epp_xml.check(xml_params) end - def domain_transfer_xml(xml_params = {}, op = 'query') + def domain_transfer_xml(xml_params = {}, op = 'query', custom_params = {}) defaults = { name: { value: 'example.ee' }, authInfo: { @@ -289,7 +289,7 @@ module Epp xml_params = defaults.deep_merge(xml_params) epp_xml = EppXml::Domain.new(cl_trid: 'ABC-12345') - epp_xml.transfer(xml_params, op) + epp_xml.transfer(xml_params, op, custom_params) end def log(req, res)