diff --git a/app/helpers/epp/domains_helper.rb b/app/helpers/epp/domains_helper.rb
index 2abf3aa27..566f5ebd2 100644
--- a/app/helpers/epp/domains_helper.rb
+++ b/app/helpers/epp/domains_helper.rb
@@ -38,6 +38,9 @@ module Epp::DomainsHelper
@domain = find_domain
handle_errors(@domain) and return unless @domain
+ handle_errors(@domain) and return unless @domain.attach_objects(@ph, parsed_frame.css('add'))
+ handle_errors(@domain) and return unless @domain.detach_objects(@ph, parsed_frame.css('rem'))
+ handle_errors(@domain) and return unless @domain.save
render '/epp/domains/success'
end
diff --git a/app/models/concerns/epp_errors.rb b/app/models/concerns/epp_errors.rb
index e8c56918e..cb7f53dd2 100644
--- a/app/models/concerns/epp_errors.rb
+++ b/app/models/concerns/epp_errors.rb
@@ -51,8 +51,12 @@ module EppErrors
end
def find_epp_code(msg)
- self.class::EPP_CODE_MAP.each do |code, values|
- return code if values.include?(msg)
+ epp_code_map.each do |code, values|
+ values.each do |x|
+ t = errors.generate_message(*x) if x.is_a?(Array)
+ t = x if x.is_a?(String)
+ return code if t == msg
+ end
end
nil
end
diff --git a/app/models/contact.rb b/app/models/contact.rb
index 81938499b..d114f90a8 100644
--- a/app/models/contact.rb
+++ b/app/models/contact.rb
@@ -4,13 +4,6 @@ class Contact < ActiveRecord::Base
include EppErrors
- EPP_CODE_MAP = {
- '2302' => ['Contact id already exists'],
- '2303' => [:not_found, :epp_obj_does_not_exist],
- '2305' => ['Object association prohibits operation'],
- '2005' => ['Phone nr is invalid', 'Email is invalid']
- }
-
EPP_ATTR_MAP = {}
has_one :address
@@ -88,7 +81,7 @@ class Contact < ActiveRecord::Base
relation = get_relation(model)
return true unless relation.nil? || relation.blank?
false
- end
+ end
#should use only in transaction
def destroy_and_clean
@@ -101,27 +94,35 @@ class Contact < ActiveRecord::Base
destroy
end
+ def epp_code_map
+ {
+ '2302' => [[:code, :epp_id_taken]],
+ '2303' => [:not_found, :epp_obj_does_not_exist],
+ '2005' => ['Phone nr is invalid', 'Email is invalid']
+ }
+ end
+
class << self
def extract_attributes ph, type=:create
-
+
contact_hash = {
phone: ph[:voice],
ident: ph[:ident],
email: ph[:email]
}
-
+
contact_hash = contact_hash.merge({
name: ph[:postalInfo][:name],
org_name: ph[:postalInfo][:org]
}) if ph[:postalInfo].is_a? Hash
-
+
contact_hash[:code] = ph[:id] if type == :create
-
+
contact_hash.delete_if { |k, v| v.nil? }
end
-
+
def check_availability(codes)
codes = [codes] if codes.is_a?(String)
diff --git a/app/models/domain.rb b/app/models/domain.rb
index a3775a244..1f52863ed 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -4,14 +4,6 @@ class Domain < ActiveRecord::Base
include EppErrors
- EPP_CODE_MAP = {
- '2302' => ['Domain name already exists', 'Domain name is reserved or restricted'], # Object exists
- '2306' => ['Registrant is missing', 'Admin contact is missing', 'Given and current expire dates do not match'], # Parameter policy error
- '2004' => ['Nameservers count must be between 1-13', 'Period must add up to 1, 2 or 3 years'], # Parameter value range error
- '2303' => ['Registrant not found', 'Contact was not found'], # Object does not exist
- '2200' => ['Authentication error']
- }
-
EPP_ATTR_MAP = {
owner_contact: 'registrant',
name_dirty: 'name',
@@ -33,6 +25,10 @@ class Domain < ActiveRecord::Base
has_and_belongs_to_many :nameservers
+ has_many :domain_statuses, -> {
+ joins(:setting).where(settings: {setting_group_id: SettingGroup.domain_statuses.id})
+ }
+
delegate :code, to: :owner_contact, prefix: true
delegate :name, to: :registrar, prefix: true
@@ -53,16 +49,21 @@ class Domain < ActiveRecord::Base
write_attribute(:name_dirty, value)
end
- ### CREATE ###
+ ### CREATE & UPDATE ###
def attach_objects(ph, parsed_frame)
- attach_owner_contact(ph[:registrant])
+ attach_owner_contact(ph[:registrant]) if ph[:registrant]
attach_contacts(self.class.parse_contacts_from_frame(parsed_frame))
attach_nameservers(self.class.parse_nameservers_from_frame(parsed_frame))
+ attach_statuses(self.class.parse_statuses_from_frame(parsed_frame))
errors.empty?
end
+ def detach_objects(ph, parsed_frame)
+ detach_nameservers(self.class.parse_nameservers_from_frame(parsed_frame))
+ end
+
def attach_owner_contact(code)
self.owner_contact = Contact.find_by(code: code)
@@ -109,6 +110,25 @@ class Domain < ActiveRecord::Base
end
end
+ def attach_statuses(status_list)
+ status_list.each do |x|
+ setting = SettingGroup.domain_statuses.settings.find_by(value: x[:value])
+ self.domain_statuses.build(
+ setting: setting,
+ description: x[:description]
+ )
+ end
+ end
+
+ def detach_nameservers(ns_list)
+ to_delete = []
+ ns_list.each do |ns_attrs|
+ to_delete << self.nameservers.where(ns_attrs)
+ end
+
+ self.nameservers.delete(to_delete)
+ end
+
### RENEW ###
def renew(cur_exp_date, period, unit='y')
@@ -127,8 +147,9 @@ class Domain < ActiveRecord::Base
### VALIDATIONS ###
def validate_nameservers_count
- sg = SettingGroup.find_by(code: SettingGroup::DOMAIN_VALIDATION_CODE)
- min, max = sg.get(:ns_min_count).to_i, sg.get(:ns_max_count).to_i
+ sg = SettingGroup.domain_validation
+ min, max = sg.setting(:ns_min_count).value.to_i, sg.setting(:ns_max_count).value.to_i
+
unless nameservers.length.between?(min, max)
errors.add(:nameservers, :out_of_range, {min: min, max: max})
end
@@ -159,6 +180,33 @@ class Domain < ActiveRecord::Base
}) if cur_exp_date.to_date != valid_to
end
+ def epp_code_map
+ domain_validation_sg = SettingGroup.domain_validation
+
+ {
+ '2302' => [ # Object exists
+ [:name_dirty, :taken],
+ [:name_dirty, :reserved]
+ ],
+ '2306' => [ # Parameter policy error
+ [:owner_contact, :blank],
+ [:admin_contacts, :blank],
+ [:valid_to, :epp_exp_dates_do_not_match]
+ ],
+ '2004' => [ # Parameter value range error
+ [:nameservers, :out_of_range, {min: domain_validation_sg.setting(:ns_min_count).value, max: domain_validation_sg.setting(:ns_max_count).value}],
+ [:period, :out_of_range]
+ ],
+ '2303' => [ # Object does not exist
+ [:owner_contact, :epp_registrant_not_found],
+ [:domain_contacts, :not_found]
+ ],
+ '2200' => [
+ [:auth_info, :wrong_pw]
+ ]
+ }
+ end
+
## SHARED
# For domain transfer
@@ -211,6 +259,18 @@ class Domain < ActiveRecord::Base
p[:unit]
end
+ def parse_statuses_from_frame(parsed_frame)
+ res = []
+
+ parsed_frame.css('status').each do |x|
+ res << {
+ value: x['s'],
+ description: x.text
+ }
+ end
+ res
+ end
+
def check_availability(domains)
domains = [domains] if domains.is_a?(String)
@@ -227,7 +287,7 @@ class Domain < ActiveRecord::Base
end
if Domain.find_by(name: x)
- res << {name: x, avail: 0, reason: 'in use'} #confirm reason with current API
+ res << {name: x, avail: 0, reason: 'in use'}
else
res << {name: x, avail: 1}
end
diff --git a/app/models/domain_status.rb b/app/models/domain_status.rb
new file mode 100644
index 000000000..80c898161
--- /dev/null
+++ b/app/models/domain_status.rb
@@ -0,0 +1,21 @@
+class DomainStatus < ActiveRecord::Base
+ # Domain statuses are stored as settings
+ include EppErrors
+
+ EPP_ATTR_MAP = {
+ setting: 'status'
+ }
+
+ belongs_to :domain
+ belongs_to :setting
+
+ delegate :value, :code, to: :setting
+
+ validates :setting, uniqueness: { scope: :domain_id }
+
+ def epp_code_map
+ {
+ '2302' => [[:setting, :taken]]
+ }
+ end
+end
diff --git a/app/models/nameserver.rb b/app/models/nameserver.rb
index e033d29b4..e3cf257c8 100644
--- a/app/models/nameserver.rb
+++ b/app/models/nameserver.rb
@@ -1,10 +1,6 @@
class Nameserver < ActiveRecord::Base
include EppErrors
- EPP_CODE_MAP = {
- '2005' => ['Hostname is invalid', 'IPv4 is invalid', 'IPv6 is invalid']
- }
-
EPP_ATTR_MAP = {
hostname: 'hostName'
}
@@ -15,4 +11,14 @@ class Nameserver < ActiveRecord::Base
validates :hostname, format: { with: /\A(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\z/ }
validates :ipv4, format: { with: /\A(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/, allow_nil: true }
validates :ipv6, format: { with: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, allow_nil: true }
+
+ def epp_code_map
+ {
+ '2005' => [
+ [:hostname, :invalid],
+ [:ipv4, :invalid],
+ [:ipv6, :invalid]
+ ]
+ }
+ end
end
diff --git a/app/models/setting.rb b/app/models/setting.rb
index 3bdda2a0c..eb4dbe7f5 100644
--- a/app/models/setting.rb
+++ b/app/models/setting.rb
@@ -1,4 +1,6 @@
class Setting < ActiveRecord::Base
belongs_to :setting_group
+ has_many :domain_statuses
+ has_many :domains, through: :domain_statuses
validates :code, uniqueness: { scope: :setting_group_id }
end
diff --git a/app/models/setting_group.rb b/app/models/setting_group.rb
index 0435677f8..3fea54616 100644
--- a/app/models/setting_group.rb
+++ b/app/models/setting_group.rb
@@ -3,10 +3,19 @@ class SettingGroup < ActiveRecord::Base
accepts_nested_attributes_for :settings
- DOMAIN_VALIDATION_CODE = 'domain_validation'
+ validates :code, uniqueness: true
- def get(key)
- s = settings.find_by(code: key.to_s)
- s.try(:value)
+ def setting(key)
+ settings.find_by(code: key.to_s)
+ end
+
+ class << self
+ def domain_validation
+ find_by(code: 'domain_validation')
+ end
+
+ def domain_statuses
+ find_by(code: 'domain_statuses')
+ end
end
end
diff --git a/app/views/setting_groups/index.haml b/app/views/setting_groups/index.haml
index 492d76d54..07bbdcafd 100644
--- a/app/views/setting_groups/index.haml
+++ b/app/views/setting_groups/index.haml
@@ -1,14 +1,14 @@
%h2= t('shared.setting_groups')
%hr
-- @setting_groups.each do |x|
- .row
- .col-md-12
- %table.table.table-striped.table-bordered
- %tr
- %th{class: 'col-xs-9'}
- = t('.setting_group')
- %th{class: 'col-xs-2'}
- = t('shared.action')
+.row
+ .col-md-12
+ %table.table.table-striped.table-bordered
+ %tr
+ %th{class: 'col-xs-9'}
+ = t('.setting_group')
+ %th{class: 'col-xs-2'}
+ = t('shared.action')
+ - @setting_groups.each do |x|
%tr
%td= t("setting_groups.codes.#{x.code}")
%td= link_to(t('.edit_settings'), setting_group_path(x), class: 'btn btn-primary btn-xs')
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f12ea140c..1214bbd9c 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -50,8 +50,6 @@ en:
blank: 'Admin contact is missing'
nameservers:
out_of_range: 'Nameservers count must be between %{min}-%{max}'
- hostname_invalid: 'Hostname is invalid'
- ip_invalid: 'IPv4 is invalid'
period:
out_of_range: 'Period must add up to 1, 2 or 3 years'
auth_info:
@@ -68,6 +66,12 @@ en:
attributes:
code:
taken: 'Code already exists'
+ domain_status:
+ attributes:
+ setting:
+ taken: 'Status already exists on this domain'
+ value:
+ taken: 'Status already exists on this domain'
attributes:
domain:
name: 'Domain name'
diff --git a/db/migrate/20140819095802_create_domains_statuses.rb b/db/migrate/20140819095802_create_domains_statuses.rb
new file mode 100644
index 000000000..bd7323982
--- /dev/null
+++ b/db/migrate/20140819095802_create_domains_statuses.rb
@@ -0,0 +1,9 @@
+class CreateDomainsStatuses < ActiveRecord::Migration
+ def change
+ create_table :domain_statuses do |t|
+ t.integer :domain_id
+ t.integer :setting_id
+ t.string :description
+ end
+ end
+end
diff --git a/db/migrate/20140819103517_populate_domain_statuses.rb b/db/migrate/20140819103517_populate_domain_statuses.rb
new file mode 100644
index 000000000..51695125f
--- /dev/null
+++ b/db/migrate/20140819103517_populate_domain_statuses.rb
@@ -0,0 +1,25 @@
+class PopulateDomainStatuses < ActiveRecord::Migration
+ def change
+ sg = SettingGroup.create(code: 'domain_statuses')
+ sg.settings = [
+ Setting.create(code: 'clientDeleteProhibited'.underscore, value: 'clientDeleteProhibited'),
+ Setting.create(code: 'serverDeleteProhibited'.underscore, value: 'serverDeleteProhibited'),
+ Setting.create(code: 'clientHold'.underscore, value: 'clientHold'),
+ Setting.create(code: 'serverHold'.underscore, value: 'serverHold'),
+ Setting.create(code: 'clientRenewProhibited'.underscore, value: 'clientRenewProhibited'),
+ Setting.create(code: 'serverRenewProhibited'.underscore, value: 'serverRenewProhibited'),
+ Setting.create(code: 'clientTransferProhibited'.underscore, value: 'clientTransferProhibited'),
+ Setting.create(code: 'serverTransferProhibited'.underscore, value: 'serverTransferProhibited'),
+ Setting.create(code: 'clientUpdateProhibited'.underscore, value: 'clientUpdateProhibited'),
+ Setting.create(code: 'serverUpdateProhibited'.underscore, value: 'serverUpdateProhibited'),
+ Setting.create(code: 'inactive', value: 'inactive'),
+ Setting.create(code: 'ok', value: 'ok'),
+ Setting.create(code: 'pendingCreate'.underscore, value: 'pendingCreate'),
+ Setting.create(code: 'pendingDelete'.underscore, value: 'pendingDelete'),
+ Setting.create(code: 'pendingRenew'.underscore, value: 'pendingRenew'),
+ Setting.create(code: 'pendingTransfer'.underscore, value: 'pendingTransfer'),
+ Setting.create(code: 'pendingUpdate'.underscore, value: 'pendingUpdate')
+ ]
+ sg.save
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71db15ed3..71d2b04e3 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: 20140815114000) do
+ActiveRecord::Schema.define(version: 20140819103517) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -61,6 +61,12 @@ ActiveRecord::Schema.define(version: 20140815114000) do
t.datetime "updated_at"
end
+ create_table "domain_statuses", force: true do |t|
+ t.integer "domain_id"
+ t.integer "setting_id"
+ t.string "description"
+ end
+
create_table "domains", force: true do |t|
t.string "name"
t.integer "registrar_id"
diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb
index 87d5a5f60..0ef3e6671 100644
--- a/spec/epp/domain_spec.rb
+++ b/spec/epp/domain_spec.rb
@@ -7,6 +7,7 @@ describe 'EPP Domain', epp: true do
before(:each) do
Fabricate(:epp_user)
Fabricate(:domain_validation_setting_group)
+ Fabricate(:domain_statuses_setting_group)
end
it 'returns error if contact does not exists' do
@@ -244,13 +245,51 @@ describe 'EPP Domain', epp: true do
expect(response[:results][0][:msg]).to eq('Domain not found')
end
- it 'updates domain', pending: true do
- response = epp_request('domains/update.xml')
+ it 'updates domain and adds objects' do
+ response = epp_request('domains/update_add_objects.xml')
+ expect(response[:results][0][:result_code]).to eq('2303')
+ expect(response[:results][0][:msg]).to eq('Contact was not found')
+
+ Fabricate(:contact, code: 'mak21')
+
+ response = epp_request('domains/update_add_objects.xml')
expect(response[:results][0][:result_code]).to eq('1000')
d = Domain.first
- new_ns = d.nameservers.find_by(hostname: 'ns2.example.com')
+
+ new_ns_count = d.nameservers.where(hostname: ['ns1.example.com', 'ns2.example.com']).count
+ expect(new_ns_count).to eq(2)
+
+ new_contact = d.tech_contacts.find_by(code: 'mak21')
+ expect(new_contact).to be_truthy
+
+ expect(d.domain_statuses.count).to eq(2)
+ expect(d.domain_statuses.first.description).to eq('Payment overdue.')
+ expect(d.domain_statuses.first.value).to eq('clientHold')
+ expect(d.domain_statuses.first.code).to eq('client_hold')
+
+ expect(d.domain_statuses.last.value).to eq('clientUpdateProhibited')
+
+ response = epp_request('domains/update_add_objects.xml')
+
+ expect(response[:results][0][:result_code]).to eq('2302')
+ expect(response[:results][0][:msg]).to eq('Status already exists on this domain')
+ expect(d.domain_statuses.count).to eq(2)
+ end
+
+ it 'updates a domain and removes objects' do
+ Fabricate(:contact, code: 'mak21')
+ epp_request('domains/update_add_objects.xml')
+
+ d = Domain.last
+
+ new_ns = d.nameservers.find_by(hostname: 'ns1.example.com')
expect(new_ns).to be_truthy
+
+ response = epp_request('domains/update_remove_objects.xml')
+
+ rem_ns = d.nameservers.find_by(hostname: 'ns1.example.com')
+ expect(rem_ns).to be_falsey
end
end
@@ -272,7 +311,7 @@ describe 'EPP Domain', epp: true do
expect(name.text).to eq('example.ee')
expect(name[:avail]).to eq('0')
- expect(reason.text).to eq('in use') #confirm this with current API
+ expect(reason.text).to eq('in use')
end
it 'checks multiple domains' do
diff --git a/spec/epp/requests/domains/update.xml b/spec/epp/requests/domains/update.xml
index 0c4bd59dd..d3a5b9a6c 100644
--- a/spec/epp/requests/domains/update.xml
+++ b/spec/epp/requests/domains/update.xml
@@ -7,6 +7,7 @@
example.ee
+ ns1.example.com
ns2.example.com
mak21
diff --git a/spec/epp/requests/domains/update_add_objects.xml b/spec/epp/requests/domains/update_add_objects.xml
new file mode 100644
index 000000000..8cfd17616
--- /dev/null
+++ b/spec/epp/requests/domains/update_add_objects.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ example.ee
+
+
+ ns1.example.com
+ ns2.example.com
+
+ mak21
+ Payment overdue.
+
+
+
+
+ ABC-12345
+
+
diff --git a/spec/epp/requests/domains/update_remove_objects.xml b/spec/epp/requests/domains/update_remove_objects.xml
new file mode 100644
index 000000000..fd79586c3
--- /dev/null
+++ b/spec/epp/requests/domains/update_remove_objects.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ example.ee
+
+
+ ns1.example.com
+
+ sh8013
+
+
+
+
+ ABC-12345
+
+
diff --git a/spec/fabricators/setting_group_fabricator.rb b/spec/fabricators/setting_group_fabricator.rb
index 0a05b7125..f4bb09221 100644
--- a/spec/fabricators/setting_group_fabricator.rb
+++ b/spec/fabricators/setting_group_fabricator.rb
@@ -13,3 +13,11 @@ Fabricator(:domain_validation_setting_group, from: :setting_group) do
Fabricate(:setting, code: 'ns_max_count', value: 13)
]}
end
+
+Fabricator(:domain_statuses_setting_group, from: :setting_group) do
+ code 'domain_statuses'
+ settings { [
+ Fabricate(:setting, code: 'client_hold', value: 'clientHold'),
+ Fabricate(:setting, code: 'client_update_prohibited', value: 'clientUpdateProhibited')
+ ]}
+end
diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb
index 244fe75c1..2f674de5e 100644
--- a/spec/models/domain_spec.rb
+++ b/spec/models/domain_spec.rb
@@ -48,6 +48,19 @@ describe Domain do
admin_contacts: ["Admin contact is missing"],
nameservers: ["Nameservers count must be between 1-13"]
})
+
+ sg = SettingGroup.domain_validation
+ min = sg.setting(:ns_min_count)
+ max = sg.setting(:ns_max_count)
+
+ min.value = 2
+ min.save
+
+ max.value = 7
+ max.save
+
+ expect(d.valid?).to be false
+ expect(d.errors.messages[:nameservers]).to eq(['Nameservers count must be between 2-7'])
end
it 'does not create a reserved domain' do
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
index 5aaa13b50..3a8e60330 100644
--- a/spec/models/setting_spec.rb
+++ b/spec/models/setting_spec.rb
@@ -13,7 +13,7 @@ describe Setting do
err = sg.settings.last.errors[:code].first
expect(err).to eq('Code already exists')
- sg_2 = Fabricate(:setting_group)
+ sg_2 = Fabricate(:setting_group, code: 'domain_statuses')
sg_2.settings.build(code: 'this_is_code')
expect(sg_2.save).to be true