diff --git a/Gemfile b/Gemfile index a69ae00e4..57b6ab9c9 100644 --- a/Gemfile +++ b/Gemfile @@ -89,7 +89,7 @@ group :development, :test do gem 'epp', '~> 1.4.0' # EPP XMLs - gem 'epp-xml', '~> 0.5.0' + gem 'epp-xml', '~> 0.7.0' # Replacement for fixtures gem 'fabrication', '~> 2.11.3' diff --git a/Gemfile.lock b/Gemfile.lock index 7787dac0d..bcf4aedcb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -93,7 +93,7 @@ GEM epp (1.4.0) hpricot libxml-ruby - epp-xml (0.5.0) + epp-xml (0.7.0) activesupport (~> 4.1) builder (~> 3.2) equalizer (0.0.9) @@ -365,7 +365,7 @@ DEPENDENCIES database_cleaner (~> 1.3.0) devise (~> 3.3.0) epp (~> 1.4.0) - epp-xml (~> 0.5.0) + epp-xml (~> 0.7.0) fabrication (~> 2.11.3) faker (~> 1.3.0) guard (~> 2.6.1) diff --git a/app/controllers/epp/commands_controller.rb b/app/controllers/epp/commands_controller.rb index abcb0792f..ccaad7f32 100644 --- a/app/controllers/epp/commands_controller.rb +++ b/app/controllers/epp/commands_controller.rb @@ -2,6 +2,7 @@ class Epp::CommandsController < ApplicationController include Epp::Common include Epp::DomainsHelper include Epp::ContactsHelper + include Epp::PollHelper include Shared::UserStamper helper WhodunnitHelper diff --git a/app/helpers/epp/domains_helper.rb b/app/helpers/epp/domains_helper.rb index 777b8f415..9d049e7ed 100644 --- a/app/helpers/epp/domains_helper.rb +++ b/app/helpers/epp/domains_helper.rb @@ -80,7 +80,9 @@ module Epp::DomainsHelper @domain = find_domain(secure: false) handle_errors(@domain) and return unless @domain - handle_errors(@domain) and return unless @domain.transfer(domain_transfer_params) + + @domain_transfer = @domain.transfer(domain_transfer_params) + handle_errors(@domain) and return unless @domain_transfer render '/epp/domains/transfer' end diff --git a/app/helpers/epp/poll_helper.rb b/app/helpers/epp/poll_helper.rb new file mode 100644 index 000000000..354455219 --- /dev/null +++ b/app/helpers/epp/poll_helper.rb @@ -0,0 +1,41 @@ +module Epp::PollHelper + def poll + req_poll if parsed_frame.css('poll').first['op'] == 'req' + ack_poll if parsed_frame.css('poll').first['op'] == 'ack' + end + + def req_poll + @message = current_epp_user.queued_messages.last + render 'epp/poll/poll_no_messages' and return unless @message + + if @message.attached_obj_type && @message.attached_obj_id + @object = Object.const_get(@message.attached_obj_type).find(@message.attached_obj_id) + end + render 'epp/poll/poll_req' + end + + def ack_poll + @message = current_epp_user.queued_messages.find_by(id: parsed_frame.css('poll').first['msgID']) + + unless @message + epp_errors << { + code: '2303', + msg: I18n.t('message_was_not_found'), + value: { obj: 'msgID', val: parsed_frame.css('poll').first['msgID'] } + } + handle_errors and return + end + + handle_errors(@message) and return unless @message.dequeue + render 'epp/poll/poll_ack' + end + + private + + def validate__poll_request + op = parsed_frame.css('poll').first[:op] + return true if %w(ack req).include?(op) + epp_errors << { code: '2306', msg: I18n.t('errors.messages.attribute_op_is_invalid') } + false + end +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/models/domain_transfer.rb b/app/models/domain_transfer.rb index 010d67046..877a420a7 100644 --- a/app/models/domain_transfer.rb +++ b/app/models/domain_transfer.rb @@ -13,6 +13,8 @@ class DomainTransfer < ActiveRecord::Base before_create :set_wait_until + delegate :name, :valid_to, to: :domain, prefix: true + def set_wait_until wait_time = Setting.transfer_wait_time return if wait_time == 0 diff --git a/app/models/epp/epp_domain.rb b/app/models/epp/epp_domain.rb index c2cc3958d..a7f27314d 100644 --- a/app/models/epp/epp_domain.rb +++ b/app/models/epp/epp_domain.rb @@ -285,7 +285,12 @@ class Epp::EppDomain < Domain pt = pending_transfer if pt && params[:action] == 'approve' - return approve_pending_transfer(params[:current_user]) + if approve_pending_transfer(params[:current_user]) + return pt.reload + else + return false + end + end if !pt && params[:action] != 'query' @@ -297,17 +302,24 @@ class Epp::EppDomain < Domain return false unless can_be_transferred_to?(params[:current_user].registrar) end - return true if pt + return pt if pt if Setting.transfer_wait_time > 0 - domain_transfers.create( + 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 - domain_transfers.create( + dt = domain_transfers.create( status: DomainTransfer::SERVER_APPROVED, transfer_requested_at: Time.zone.now, transferred_at: Time.zone.now, @@ -320,6 +332,8 @@ class Epp::EppDomain < Domain self.registrar = params[:current_user].registrar save(validate: false) end + + dt end # rubocop: enable Metrics/PerceivedComplexity # rubocop: enable Metrics/MethodLength diff --git a/app/models/epp_user.rb b/app/models/epp_user.rb index 3a3d65626..08747eaae 100644 --- a/app/models/epp_user.rb +++ b/app/models/epp_user.rb @@ -16,5 +16,9 @@ class EppUser < ActiveRecord::Base def to_s username end + + def queued_messages + registrar.messages.queued + end end # rubocop: enable Metrics/ClassLength diff --git a/app/models/message.rb b/app/models/message.rb new file mode 100644 index 000000000..9813479e4 --- /dev/null +++ b/app/models/message.rb @@ -0,0 +1,12 @@ +class Message < ActiveRecord::Base + belongs_to :registrar + + before_create -> { self.queued = true } + + scope :queued, -> { where(queued: true) } + + def dequeue + self.queued = false + save + end +end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 72398d878..cc2c089a0 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -4,6 +4,7 @@ class Registrar < ActiveRecord::Base has_many :contacts, dependent: :restrict_with_error has_many :epp_users, dependent: :restrict_with_error has_many :users, dependent: :restrict_with_error + has_many :messages validates :name, :reg_no, :address, :country, presence: true validates :name, :reg_no, uniqueness: true diff --git a/app/views/epp/domains/partials/_transfer.xml.builder b/app/views/epp/domains/partials/_transfer.xml.builder new file mode 100644 index 000000000..6972210c1 --- /dev/null +++ b/app/views/epp/domains/partials/_transfer.xml.builder @@ -0,0 +1,9 @@ +builder.tag!('domain:trnData', 'xmlns:domain' => 'urn:ietf:params:xml:ns:domain-1.0') do + builder.tag!('domain:name', dt.domain_name) + builder.tag!('domain:trStatus', dt.status) + builder.tag!('domain:reID', dt.transfer_to.reg_no) + builder.tag!('domain:reDate', dt.transfer_requested_at) + builder.tag!('domain:acID', dt.transfer_from.reg_no) + builder.tag!('domain:acDate', dt.transferred_at || dt.wait_until) + builder.tag!('domain:exDate', dt.domain_valid_to) +end diff --git a/app/views/epp/domains/transfer.xml.builder b/app/views/epp/domains/transfer.xml.builder index 9a7ff183d..bea166939 100644 --- a/app/views/epp/domains/transfer.xml.builder +++ b/app/views/epp/domains/transfer.xml.builder @@ -5,16 +5,7 @@ xml.epp_head do end xml.resData do - xml.tag!('domain:trnData', 'xmlns:domain' => 'urn:ietf:params:xml:ns:domain-1.0') do - xml.tag!('domain:name', @domain.name) - ldt = @domain.domain_transfers.last - xml.tag!('domain:trStatus', ldt.status) - xml.tag!('domain:reID', ldt.transfer_to.reg_no) - xml.tag!('domain:reDate', ldt.transfer_requested_at) - xml.tag!('domain:acID', ldt.transfer_from.reg_no) - xml.tag!('domain:acDate', ldt.transferred_at || ldt.wait_until) - xml.tag!('domain:exDate', @domain.valid_to) - end + xml << render('epp/domains/partials/transfer', builder: xml, dt: @domain_transfer) end end diff --git a/app/views/epp/poll/poll_ack.xml.builder b/app/views/epp/poll/poll_ack.xml.builder new file mode 100644 index 000000000..07a09cda1 --- /dev/null +++ b/app/views/epp/poll/poll_ack.xml.builder @@ -0,0 +1,11 @@ +xml.epp_head do + xml.response do + xml.result('code' => '1000') do + xml.msg 'Command completed successfully' + end + + xml.tag!('msgQ', 'count' => current_epp_user.queued_messages.count, 'id' => @message.id) + + xml << render('/epp/shared/trID') + end +end diff --git a/app/views/epp/poll/poll_no_messages.xml.builder b/app/views/epp/poll/poll_no_messages.xml.builder new file mode 100644 index 000000000..1e57a1216 --- /dev/null +++ b/app/views/epp/poll/poll_no_messages.xml.builder @@ -0,0 +1,9 @@ +xml.epp_head do + xml.response do + xml.result('code' => '1300') do + xml.msg 'Command completed successfully; no messages' + end + + xml << render('/epp/shared/trID') + end +end diff --git a/app/views/epp/poll/poll_req.xml.builder b/app/views/epp/poll/poll_req.xml.builder new file mode 100644 index 000000000..15d5cab6f --- /dev/null +++ b/app/views/epp/poll/poll_req.xml.builder @@ -0,0 +1,20 @@ +xml.epp_head do + xml.response do + xml.result('code' => '1301') do + xml.msg 'Command completed successfully; ack to dequeue' + end + + xml.tag!('msgQ', 'count' => current_epp_user.queued_messages.count, 'id' => @message.id) do + xml.qDate @message.created_at + xml.msg @message.body + end + + xml.resData do + if @message.attached_obj_type == 'DomainTransfer' + xml << render('epp/domains/partials/transfer', builder: xml, dt: @object) + end + end if @object + + xml << render('/epp/shared/trID') + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index ed86eb006..da8dc2c87 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -419,5 +419,6 @@ en: ds_algorithm: 'DS algorithm' setting: 'Setting' - registrar: Registrar - + registrar: 'Registrar' + transfer_requested: 'Transfer requested.' + message_was_not_found: 'Message was not found' diff --git a/db/migrate/20141105150721_create_messages.rb b/db/migrate/20141105150721_create_messages.rb new file mode 100644 index 000000000..738e4d363 --- /dev/null +++ b/db/migrate/20141105150721_create_messages.rb @@ -0,0 +1,13 @@ +class CreateMessages < ActiveRecord::Migration + def change + create_table :messages do |t| + t.integer :registrar_id + t.string :body + t.string :attached_obj_type + t.string :attached_obj_id + t.boolean :queued + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 09125780b..c436862b9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20141015135742) do +ActiveRecord::Schema.define(version: 20141105150721) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -199,6 +199,16 @@ ActiveRecord::Schema.define(version: 20141015135742) do t.datetime "updated_at" end + create_table "messages", force: true do |t| + t.integer "registrar_id" + t.string "body" + t.string "attached_obj_type" + t.string "attached_obj_id" + t.boolean "queued" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "nameserver_versions", force: true do |t| t.string "item_type", null: false t.integer "item_id", null: false diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 3eda9bec9..cbab222e2 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -27,7 +27,7 @@ describe 'EPP Domain', epp: true do it 'returns error if contact does not exists' do Fabricate(:contact, code: 'jd1234') - response = epp_request(EppXml::Domain.create, :xml) + response = epp_request(domain_create_xml, :xml) expect(response[:results][0][:result_code]).to eq('2303') expect(response[:results][0][:msg]).to eq('Contact was not found') @@ -48,14 +48,14 @@ describe 'EPP Domain', epp: true do end it 'can not see other registrar domains' do - response = epp_request(EppXml::Domain.info, :xml, :elkdata) + response = epp_request(domain_info_xml, :xml, :elkdata) expect(response[:result_code]).to eq('2302') expect(response[:msg]).to eq('Domain exists but belongs to other registrar') end it 'transfers a domain' do pw = domain.auth_info - xml = domain_transfer_xml(pw: pw) + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }) response = epp_request(xml, :xml, :elkdata) domain.reload @@ -76,7 +76,7 @@ describe 'EPP Domain', epp: true do domain.reload pw = domain.auth_info - xml = domain_transfer_xml(pw: pw) # request with new password + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }) # request with new password response = epp_request(xml, :xml, :zone) trn_data = response[:parsed].css('trnData') @@ -110,6 +110,26 @@ describe 'EPP Domain', epp: true do expect(trn_data.css('exDate').text).to eq(domain.valid_to.to_time.utc.to_s) expect(domain.registrar).to eq(elkdata) + + # should show up in other registrar's poll + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.css('qDate').text).to_not be_blank + expect(msg_q.css('msg').text).to eq('Transfer requested.') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('1') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') end it 'prohibits wrong registrar from approving transfer' do @@ -120,7 +140,7 @@ describe 'EPP Domain', epp: true do transfer_from: zone }) - xml = domain_transfer_xml(pw: domain.auth_info, op: 'approve') + xml = domain_transfer_xml({ authInfo: { pw: { value: domain.auth_info } } }, 'approve') response = epp_request(xml, :xml, :elkdata) expect(response[:result_code]).to eq('2304') expect(response[:msg]).to eq('Transfer can be approved only by current domain registrar') @@ -134,7 +154,7 @@ describe 'EPP Domain', epp: true do transfer_from: zone }) - xml = domain_transfer_xml(pw: domain.auth_info, op: 'approve') + xml = domain_transfer_xml({ authInfo: { pw: { value: domain.auth_info } } }, 'approve') response = epp_request(xml, :xml, :zone) domain.reload dtl = domain.domain_transfers.last @@ -150,14 +170,15 @@ describe 'EPP Domain', epp: true do end it 'does not transfer with invalid pw' do - response = epp_request(domain_transfer_xml(pw: 'test'), :xml) + xml = domain_transfer_xml({ authInfo: { pw: { value: 'test' } } }) + response = epp_request(xml, :xml) expect(response[:result_code]).to eq('2201') expect(response[:msg]).to eq('Authorization error') end it 'ignores transfer when owner registrar requests transfer' do pw = domain.auth_info - xml = domain_transfer_xml(pw: pw) + xml = domain_transfer_xml({ authInfo: { pw: { value: pw } } }) response = epp_request(xml, :xml, :zone) expect(response[:result_code]).to eq('2002') @@ -165,7 +186,7 @@ describe 'EPP Domain', epp: true do end it 'returns an error for incorrect op attribute' do - response = epp_request(domain_transfer_xml(op: 'bla'), :xml, :zone) + response = epp_request(domain_transfer_xml({}, 'bla'), :xml, :zone) expect(response[:result_code]).to eq('2306') expect(response[:msg]).to eq('Attribute op is invalid') end @@ -188,7 +209,7 @@ describe 'EPP Domain', epp: true do end it 'creates a domain' do - response = epp_request(EppXml::Domain.create, :xml) + response = epp_request(domain_create_xml, :xml) d = Domain.first expect(response[:result_code]).to eq('1000') @@ -224,7 +245,7 @@ describe 'EPP Domain', epp: true do end it 'creates ria.ee with valid ds record' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ name: { value: 'ria.ee' } }, { _anonymus: [ @@ -250,7 +271,7 @@ describe 'EPP Domain', epp: true do end it 'validates nameserver ipv4 when in same zone as domain' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ ns: [ { hostObj: { value: 'ns1.example.ee' } }, { hostObj: { value: 'ns2.example.ee' } } @@ -263,8 +284,8 @@ describe 'EPP Domain', epp: true do end it 'does not create duplicate domain' do - epp_request(EppXml::Domain.create, :xml) - response = epp_request(EppXml::Domain.create, :xml) + epp_request(domain_create_xml, :xml) + response = epp_request(domain_create_xml, :xml) expect(response[:result_code]).to eq('2302') expect(response[:msg]).to eq('Domain name already exists') expect(response[:clTRID]).to eq('ABC-12345') @@ -273,7 +294,7 @@ describe 'EPP Domain', epp: true do it 'does not create reserved domain' do Fabricate(:reserved_domain) - xml = EppXml::Domain.create(name: { value: '1162.ee' }) + xml = domain_create_xml(name: { value: '1162.ee' }) response = epp_request(xml, :xml) expect(response[:result_code]).to eq('2302') @@ -282,7 +303,7 @@ describe 'EPP Domain', epp: true do end it 'does not create domain without contacts and registrant' do - xml = EppXml::Domain.create(contacts: [], registrant: false) + xml = domain_create_xml(contacts: [], registrant: false) response = epp_request(xml, :xml) expect(response[:results][0][:result_code]).to eq('2003') @@ -290,7 +311,7 @@ describe 'EPP Domain', epp: true do end it 'does not create domain without nameservers' do - xml = EppXml::Domain.create(ns: []) + xml = domain_create_xml(ns: []) response = epp_request(xml, :xml) expect(response[:result_code]).to eq('2003') expect(response[:msg]).to eq('Required parameter missing: ns') @@ -299,7 +320,7 @@ describe 'EPP Domain', epp: true do it 'does not create domain with too many nameservers' do nameservers = [] 14.times { |i| nameservers << { hostObj: { value: "ns#{i}.example.net" } } } - xml = EppXml::Domain.create(ns: nameservers) + xml = domain_create_xml(ns: nameservers) response = epp_request(xml, :xml) expect(response[:result_code]).to eq('2004') @@ -307,7 +328,7 @@ describe 'EPP Domain', epp: true do end it 'returns error when invalid nameservers are present' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ ns: [ { hostObj: { value: 'invalid1-' } }, { hostObj: { value: '-invalid2' } } @@ -341,7 +362,7 @@ describe 'EPP Domain', epp: true do end it 'creates a domain with period in days' do - xml = EppXml::Domain.create(period_value: 365, period_unit: 'd') + xml = domain_create_xml(period_value: 365, period_unit: 'd') response = epp_request(xml, :xml) expect(response[:result_code]).to eq('1000') @@ -350,7 +371,7 @@ describe 'EPP Domain', epp: true do end it 'does not create a domain with invalid period' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ period: { value: '367', attrs: { unit: 'd' } } }) @@ -361,7 +382,7 @@ describe 'EPP Domain', epp: true do end it 'creates a domain with multiple dnskeys' do - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { keyData: { flags: { value: '257' }, @@ -411,7 +432,7 @@ describe 'EPP Domain', epp: true do it 'does not create a domain when dnskeys are invalid' do - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { keyData: { flags: { value: '250' }, @@ -463,7 +484,7 @@ describe 'EPP Domain', epp: true do end it 'does not create a domain with two identical dnskeys' do - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { keyData: { flags: { value: '257' }, @@ -492,7 +513,7 @@ describe 'EPP Domain', epp: true do it 'validated dnskeys count' do Setting.dnskeys_max_count = 1 - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { keyData: { flags: { value: '257' }, @@ -518,7 +539,7 @@ describe 'EPP Domain', epp: true do end it 'creates domain with ds data' do - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { dsData: { keyTag: { value: '12345' }, @@ -544,7 +565,7 @@ describe 'EPP Domain', epp: true do end it 'creates domain with ds data with key' do - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { dsData: { keyTag: { value: '12345' }, @@ -578,7 +599,7 @@ describe 'EPP Domain', epp: true do it 'prohibits dsData with key' do Setting.ds_data_with_key_allowed = false - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { dsData: { keyTag: { value: '12345' }, @@ -603,7 +624,7 @@ describe 'EPP Domain', epp: true do it 'prohibits dsData' do Setting.ds_data_allowed = false - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ { dsData: { keyTag: { value: '12345' }, @@ -628,7 +649,7 @@ describe 'EPP Domain', epp: true do it 'prohibits keyData' do Setting.key_data_allowed = false - xml = EppXml::Domain.create({}, { + xml = domain_create_xml({}, { _anonymus: [ keyData: { flags: { value: '0' }, @@ -652,7 +673,7 @@ describe 'EPP Domain', epp: true do end it 'creates a domain with contacts' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ _anonymus: [ { contact: { value: 'sh8013', attrs: { type: 'admin' } } } ] @@ -671,7 +692,7 @@ describe 'EPP Domain', epp: true do end it 'does not create a domain without admin contact' do - xml = EppXml::Domain.create({ + xml = domain_create_xml({ _anonymus: [ { contact: { value: 'sh8013', attrs: { type: 'tech' } } } ] @@ -733,7 +754,7 @@ describe 'EPP Domain', epp: true do end it 'sets ok status by default' do - response = epp_request(EppXml::Domain.info, :xml) + response = epp_request(domain_info_xml, :xml) inf_data = response[:parsed].css('resData infData') expect(inf_data.css('status').first[:s]).to eq('ok') end @@ -767,7 +788,7 @@ describe 'EPP Domain', epp: true do d.save - xml = EppXml::Domain.info(name: { value: 'Example.ee' }) + xml = domain_info_xml(name: { value: 'Example.ee' }) response = epp_request(xml, :xml) expect(response[:results][0][:result_code]).to eq('1000') @@ -821,20 +842,20 @@ describe 'EPP Domain', epp: true do d.touch - response = epp_request(EppXml::Domain.info, :xml) + response = epp_request(domain_info_xml, :xml) inf_data = response[:parsed].css('resData infData') expect(inf_data.css('upDate').text).to eq(d.updated_at.to_time.utc.to_s) end it 'returns error when domain can not be found' do - response = epp_request(EppXml::Domain.info(name: { value: 'test.ee' }), :xml) + response = epp_request(domain_info_xml(name: { value: 'test.ee' }), :xml) expect(response[:results][0][:result_code]).to eq('2303') expect(response[:results][0][:msg]).to eq('Domain not found') end it 'updates domain and adds objects', pending: true do - xml = EppXml::Domain.update({ + xml = domain_update_xml({ add: [ { ns: [ @@ -917,7 +938,7 @@ describe 'EPP Domain', epp: true do it 'updates a domain and removes objects' do Fabricate(:contact, code: 'mak21') - xml = EppXml::Domain.update({ + xml = domain_update_xml({ add: [ { ns: [ @@ -955,7 +976,7 @@ describe 'EPP Domain', epp: true do d = Domain.last expect(d.dnskeys.count).to eq(2) - xml = EppXml::Domain.update({ + xml = domain_update_xml({ rem: [ { ns: [ @@ -1006,7 +1027,7 @@ describe 'EPP Domain', epp: true do it 'does not add duplicate objects to domain' do Fabricate(:contact, code: 'mak21') - xml = EppXml::Domain.update({ + xml = domain_update_xml({ add: [ ns: [ { hostObj: { value: 'ns1.example.com' } } @@ -1032,7 +1053,7 @@ describe 'EPP Domain', epp: true do ] } - response = epp_request(EppXml::Domain.update(xml_params), :xml) + response = epp_request(domain_update_xml(xml_params), :xml) expect(response[:results][0][:result_code]).to eq('1000') d = Domain.last @@ -1042,7 +1063,7 @@ describe 'EPP Domain', epp: true do end it 'does not assign invalid status to domain' do - xml = EppXml::Domain.update({ + xml = domain_update_xml({ add: [ status: { value: '', attrs: { s: 'invalidStatus' } } ] @@ -1073,7 +1094,7 @@ describe 'EPP Domain', epp: true do end it 'checks a domain' do - response = epp_request(EppXml::Domain.check, :xml) + response = epp_request(domain_check_xml, :xml) expect(response[:result_code]).to eq('1000') expect(response[:msg]).to eq('Command completed successfully') @@ -1083,7 +1104,7 @@ describe 'EPP Domain', epp: true do Fabricate(:domain, name: 'example.ee', registrar: zone) - response = epp_request(EppXml::Domain.check, :xml) + response = epp_request(domain_check_xml, :xml) domain = response[:parsed].css('resData chkData cd').first name = domain.css('name').first reason = domain.css('reason').first @@ -1094,7 +1115,7 @@ describe 'EPP Domain', epp: true do end it 'checks multiple domains' do - xml = EppXml::Domain.check({ + xml = domain_check_xml({ _anonymus: [ { name: { value: 'one.ee' } }, { name: { value: 'two.ee' } }, @@ -1116,7 +1137,7 @@ describe 'EPP Domain', epp: true do end it 'checks invalid format domain' do - xml = EppXml::Domain.check({ + xml = domain_check_xml({ _anonymus: [ { name: { value: 'one.ee' } }, { name: { value: 'notcorrectdomain' } } diff --git a/spec/epp/epp_helper_spec.rb b/spec/epp/epp_helper_spec.rb index 1283c164e..0a0ccc80e 100644 --- a/spec/epp/epp_helper_spec.rb +++ b/spec/epp/epp_helper_spec.rb @@ -2,47 +2,6 @@ require 'rails_helper' describe 'EPP Helper', epp: true do context 'in context of Domain' do - it 'generates valid renew xml' do - expected = Nokogiri::XML(' - - - - - example.ee - 2014-08-07 - 1 - - - ABC-12345 - - - ').to_s.squish - - generated = Nokogiri::XML(domain_renew_xml).to_s.squish - expect(generated).to eq(expected) - - expected = Nokogiri::XML(' - - - - - one.ee - 2009-11-15 - 365 - - - ABC-12345 - - - ').to_s.squish - - generated = Nokogiri::XML(domain_renew_xml(name: 'one.ee', curExpDate: '2009-11-15', - period_value: '365', period_unit: 'd')).to_s.squish - expect(generated).to eq(expected) - end - it 'generates valid transfer xml' do expected = Nokogiri::XML(' @@ -81,7 +40,12 @@ describe 'EPP Helper', epp: true do ').to_s.squish - xml = domain_transfer_xml(name: 'one.ee', op: 'approve', pw: 'test', roid: 'askdf') + xml = domain_transfer_xml({ + name: { value: 'one.ee' }, + authInfo: { + pw: { value: 'test', attrs: { roid: 'askdf' } } + } + }, 'approve') generated = Nokogiri::XML(xml).to_s.squish expect(generated).to eq(expected) diff --git a/spec/epp/poll_spec.rb b/spec/epp/poll_spec.rb new file mode 100644 index 000000000..030b9b022 --- /dev/null +++ b/spec/epp/poll_spec.rb @@ -0,0 +1,134 @@ +require 'rails_helper' + +describe 'EPP Poll', epp: true do + let(:server_zone) { Epp::Server.new({ server: 'localhost', tag: 'zone', password: 'ghyt9e4fu', port: 701 }) } + let(:server_elkdata) { Epp::Server.new({ server: 'localhost', tag: 'elkdata', password: 'ghyt9e4fu', port: 701 }) } + let(:elkdata) { Fabricate(:registrar, { name: 'Elkdata', reg_no: '123' }) } + let(:zone) { Fabricate(:registrar) } + + before(:each) { create_settings } + + context 'with valid user' do + before(:each) do + Fabricate(:epp_user, username: 'zone', registrar: zone) + Fabricate(:epp_user, username: 'elkdata', registrar: elkdata) + end + + it 'returns no messages in poll' do + response = epp_request(EppXml::Session.poll, :xml) + + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + end + + it 'queues and dequeues messages' do + msg = zone.messages.create({ body: 'Balance low.' }) + + response = epp_request(EppXml::Session.poll, :xml, :elkdata) + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Balance low.') + expect(msg_q.first['count']).to eq('1') + expect(msg_q.first['id']).to eq(msg.id.to_s) + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :elkdata) + expect(response[:results][0][:msg]).to eq('Message was not found') + expect(response[:results][0][:result_code]).to eq('2303') + expect(response[:results][0][:value]).to eq(msg_q.first['id']) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') + + response = epp_request(xml, :xml, :zone) + expect(response[:results][0][:msg]).to eq('Message was not found') + expect(response[:results][0][:result_code]).to eq('2303') + expect(response[:results][0][:value]).to eq(msg_q.first['id']) + end + + it 'returns an error on incorrect op' do + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'bla' } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Attribute op is invalid') + end + + it 'dequeues multiple messages' do + zone.messages.create({ body: 'Balance low.' }) + zone.messages.create({ body: 'Something.' }) + zone.messages.create({ body: 'Smth else.' }) + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Smth else.') + expect(msg_q.first['count']).to eq('3') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('2') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Something.') + expect(msg_q.first['count']).to eq('2') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('1') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; ack to dequeue') + expect(response[:result_code]).to eq('1301') + msg_q = response[:parsed].css('msgQ') + + expect(msg_q.css('msg').text).to eq('Balance low.') + expect(msg_q.first['count']).to eq('1') + + xml = EppXml::Session.poll(poll: { + value: '', attrs: { op: 'ack', msgID: msg_q.first['id'] } + }) + + response = epp_request(xml, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully') + msg_q = response[:parsed].css('msgQ') + expect(msg_q.first['id']).to_not be_blank + expect(msg_q.first['count']).to eq('0') + + response = epp_request(EppXml::Session.poll, :xml, :zone) + expect(response[:msg]).to eq('Command completed successfully; no messages') + expect(response[:result_code]).to eq('1300') + end + end +end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb new file mode 100644 index 000000000..0ff0e4d92 --- /dev/null +++ b/spec/models/message_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +describe Message do + it { should belong_to(:registrar) } +end diff --git a/spec/models/registrar_spec.rb b/spec/models/registrar_spec.rb index 196857582..dc7f0922c 100644 --- a/spec/models/registrar_spec.rb +++ b/spec/models/registrar_spec.rb @@ -5,4 +5,5 @@ describe Registrar do it { should have_many(:domains) } it { should have_many(:epp_users) } it { should have_many(:users) } + it { should have_many(:messages) } end diff --git a/spec/support/epp.rb b/spec/support/epp.rb index 1f456775d..3c482d97a 100644 --- a/spec/support/epp.rb +++ b/spec/support/epp.rb @@ -52,80 +52,80 @@ module Epp ### REQUEST TEMPLATES ### - def domain_renew_xml(xml_params = {}) - xml = Builder::XmlMarkup.new + def domain_info_xml(xml_params = {}) + defaults = { + name: { value: 'example.ee', attrs: { hosts: 'all' } }, + authInfo: { + pw: { value: '2fooBAR' } + } + } - xml.instruct!(:xml, standalone: 'no') - xml.epp('xmlns' => 'urn:ietf:params:xml:ns:epp-1.0') do - xml.command do - xml.renew do - xml.tag!('domain:renew', 'xmlns:domain' => 'urn:ietf:params:xml:ns:domain-1.0') do - xml.tag!('domain:name', (xml_params[:name] || 'example.ee')) if xml_params[:name] != false - xml.tag!('domain:curExpDate', (xml_params[:curExpDate] || '2014-08-07')) if xml_params[:curExpDate] != false - - if xml_params[:period] != false - xml.tag!('domain:period', (xml_params[:period_value] || 1), 'unit' => (xml_params[:period_unit] || 'y')) - end - end - end - xml.clTRID 'ABC-12345' - end - end + xml_params = defaults.deep_merge(xml_params) + EppXml::Domain.info(xml_params) end - def generate_xml_from_hash(xml_params, xml, ns = '') - xml_params.each do |k, v| - # Value is a hash which has string type value - if v.is_a?(Hash) && v[:value].is_a?(String) - xml.tag!("#{ns}#{k}", v[:value], v[:attrs]) - # Value is a hash which is nested - elsif v.is_a?(Hash) - xml.tag!("#{ns}#{k}") do - generate_xml_from_hash(v, xml, ns) - end - # Value is an array - elsif v.is_a?(Array) - if k.to_s.start_with?('_') - v.each do |x| - generate_xml_from_hash(x, xml, ns) - end - else - xml.tag!("#{ns}#{k}") do - v.each do |x| - generate_xml_from_hash(x, xml, ns) - end - end - end - end - end + def domain_create_xml(xml_params = {}, dnssec_params = {}) + defaults = { + name: { value: 'example.ee' }, + period: { value: '1', attrs: { unit: 'y' } }, + ns: [ + { hostObj: { value: 'ns1.example.net' } }, + { hostObj: { value: 'ns2.example.net' } } + ], + registrant: { value: 'jd1234' }, + _anonymus: [ + { contact: { value: 'sh8013', attrs: { type: 'admin' } } }, + { contact: { value: 'sh8013', attrs: { type: 'tech' } } }, + { contact: { value: 'sh801333', attrs: { type: 'tech' } } } + ] + } + + xml_params = defaults.deep_merge(xml_params) + + dnssec_defaults = { + _anonymus: [ + { keyData: { + flags: { value: '257' }, + protocol: { value: '3' }, + alg: { value: '5' }, + pubKey: { value: 'AwEAAddt2AkLfYGKgiEZB5SmIF8EvrjxNMH6HtxWEA4RJ9Ao6LCWheg8' } + } + }] + } + + dnssec_params = dnssec_defaults.deep_merge(dnssec_params) if dnssec_params != false + EppXml::Domain.create(xml_params, dnssec_params) end + def domain_update_xml(xml_params = {}, dnssec_params = false) + defaults = { + name: { value: 'example.ee' } + } - def domain_transfer_xml(xml_params = {}) - xml_params[:name] = xml_params[:name] || 'example.ee' - xml_params[:pw] = xml_params[:pw] || '98oiewslkfkd' - xml_params[:op] = xml_params[:op] || 'query' - xml_params[:roid] = xml_params[:roid] || 'JD1234-REP' + xml_params = defaults.deep_merge(xml_params) + EppXml::Domain.update(xml_params, dnssec_params) + end - xml = Builder::XmlMarkup.new + def domain_check_xml(xml_params = {}) + defaults = { + _anonymus: [ + { name: { value: 'example.ee' } } + ] + } + xml_params = defaults.deep_merge(xml_params) + EppXml::Domain.check(xml_params) + end - xml.instruct!(:xml, standalone: 'no') - xml.epp('xmlns' => 'urn:ietf:params:xml:ns:epp-1.0') do - xml.command do - xml.transfer('op' => xml_params[:op]) do - xml.tag!('domain:transfer', 'xmlns:domain' => 'urn:ietf:params:xml:ns:domain-1.0') do - if xml_params[:name] != false - xml.tag!('domain:name', xml_params[:name]) - end + def domain_transfer_xml(xml_params = {}, op = 'query') + defaults = { + name: { value: 'example.ee' }, + authInfo: { + pw: { value: '98oiewslkfkd', attrs: { roid: 'JD1234-REP' } } + } + } - xml.tag!('domain:authInfo') do - xml.tag!('domain:pw', xml_params[:pw], 'roid' => xml_params[:roid]) - end if xml_params[:authInfo] != false - end - end - xml.clTRID 'ABC-12345' - end - end + xml_params = defaults.deep_merge(xml_params) + EppXml::Domain.transfer(xml_params, op) end end