Hash: select entries by keys

This commit is contained in:
Karl Erik Õunapuu 2021-01-07 13:59:12 +02:00
parent c23075fa19
commit c58b4fb2e9
No known key found for this signature in database
GPG key ID: C9DD647298A34764
11 changed files with 201 additions and 141 deletions

View file

@ -30,6 +30,9 @@ checks:
method-lines: method-lines:
config: config:
threshold: 40 threshold: 40
method-count:
config:
threshold: 25
exclude_patterns: exclude_patterns:
- "app/models/version/" - "app/models/version/"
- "bin/" - "bin/"

View file

@ -81,6 +81,11 @@ module Repp
h = {} h = {}
h[transfer_info_params[:id].match?(/\A[0-9]+\z/) ? :id : :name] = transfer_info_params[:id] h[transfer_info_params[:id].match?(/\A[0-9]+\z/) ? :id : :name] = transfer_info_params[:id]
@domain = Domain.find_by!(h) @domain = Domain.find_by!(h)
validate_registrar_authorization
end
def validate_registrar_authorization
return if @domain.registrar == current_user.registrar return if @domain.registrar == current_user.registrar
return if @domain.transfer_code.eql?(request.headers['Auth-Code']) return if @domain.transfer_code.eql?(request.headers['Auth-Code'])

View file

@ -22,13 +22,17 @@ module Domains
def update_domain def update_domain
frame_json = domain.pending_json['frame'] frame_json = domain.pending_json['frame']
user = ApiUser.find(domain.pending_json['current_user_id'])
frame = frame_json ? frame_json.with_indifferent_access : {} frame = frame_json ? frame_json.with_indifferent_access : {}
assign_domain_update_meta
Actions::DomainUpdate.new(domain, frame, true).call
end
def assign_domain_update_meta
user = ApiUser.find(domain.pending_json['current_user_id'])
domain.upid = user.registrar.id if user.registrar domain.upid = user.registrar.id if user.registrar
domain.up_date = Time.zone.now domain.up_date = Time.zone.now
Actions::DomainUpdate.new(domain, frame, true).call
end end
end end
end end

View file

@ -1,5 +1,5 @@
module Actions module Actions
class DomainCreate class DomainCreate # rubocop:disable Metrics/ClassLength
attr_reader :domain, :params attr_reader :domain, :params
def initialize(domain, params) def initialize(domain, params)
@ -14,8 +14,7 @@ module Actions
assign_registrant assign_registrant
assign_nameservers assign_nameservers
assign_admin_contacts assign_domain_contacts
assign_tech_contacts
domain.attach_default_contacts domain.attach_default_contacts
assign_expiry_time assign_expiry_time
maybe_attach_legal_doc maybe_attach_legal_doc
@ -27,24 +26,28 @@ module Actions
def validate_domain_integrity def validate_domain_integrity
return unless Domain.release_to_auction return unless Domain.release_to_auction
dn = DNS::DomainName.new(SimpleIDN.to_unicode(params[:name].strip.downcase)) dn = DNS::DomainName.new(domain.name)
if dn.at_auction? if dn.at_auction?
domain.add_epp_error('2306', nil, nil, 'Parameter value policy error: domain is at auction') domain.add_epp_error('2306', nil, nil, 'Parameter value policy error: domain is at auction')
elsif dn.awaiting_payment? elsif dn.awaiting_payment?
domain.add_epp_error('2003', nil, nil, 'Required parameter missing; reserved>pw element' \ domain.add_epp_error('2003', nil, nil, 'Required parameter missing; reserved>pw element' \
' required for reserved domains') ' required for reserved domains')
elsif dn.pending_registration? elsif dn.pending_registration?
validate_reserved_password(dn)
end
end
def validate_reserved_password(domain_name)
if params[:reserved_pw].blank? if params[:reserved_pw].blank?
domain.add_epp_error('2003', nil, nil, 'Required parameter missing; reserved>pw ' \ domain.add_epp_error('2003', nil, nil, 'Required parameter missing; reserved>pw ' \
'element is required') 'element is required')
else else
unless dn.available_with_code?(params[:reserved_pw]) unless domain_name.available_with_code?(params[:reserved_pw])
domain.add_epp_error('2202', nil, nil, 'Invalid authorization information; invalid ' \ domain.add_epp_error('2202', nil, nil, 'Invalid authorization information; invalid ' \
'reserved>pw value') 'reserved>pw value')
end end
end end
end end
end
def assign_registrant def assign_registrant
unless params[:registrant_id] unless params[:registrant_id]
@ -63,85 +66,108 @@ module Actions
def assign_domain_attributes def assign_domain_attributes
domain.name = params[:name].strip.downcase domain.name = params[:name].strip.downcase
domain.registrar = Registrar.find(params[:registrar_id]) domain.registrar = Registrar.find(params[:registrar_id])
domain.period = params[:period] assign_domain_period
domain.period_unit = params[:period_unit] assign_domain_auth_codes
domain.dnskeys_attributes = params[:dnskeys_attributes]
end
def assign_domain_auth_codes
domain.transfer_code = params[:transfer_code] if params[:transfer_code].present? domain.transfer_code = params[:transfer_code] if params[:transfer_code].present?
domain.reserved_pw = params[:reserved_pw] if params[:reserved_pw].present? domain.reserved_pw = params[:reserved_pw] if params[:reserved_pw].present?
domain.dnskeys_attributes = params[:dnskeys_attributes] end
def assign_domain_period
domain.period = params[:period]
domain.period_unit = params[:period_unit]
end end
def assign_nameservers def assign_nameservers
domain.nameservers_attributes = params[:nameservers_attributes] domain.nameservers_attributes = params[:nameservers_attributes]
end end
def assign_admin_contacts def assign_contact(contact_code, admin: true)
attrs = [] contact = Contact.find_by(code: contact_code)
params[:admin_domain_contacts_attributes].each do |c| arr = admin ? @admin_contacts : @tech_contacts
contact = Contact.find_by(code: c) if contact
domain.add_epp_error('2303', 'contact', c, %i[domain_contacts not_found]) if contact.blank? arr << { contact_id: contact.id, contact_code_cache: contact.code }
attrs << { contact_id: contact.id, contact_code_cache: contact.code } if contact else
domain.add_epp_error('2303', 'contact', contact_code, %i[domain_contacts not_found])
end
end end
domain.admin_domain_contacts_attributes = attrs def assign_domain_contacts
end @admin_contacts = []
@tech_contacts = []
params[:admin_domain_contacts_attributes].each { |c| assign_contact(c) }
params[:tech_domain_contacts_attributes].each { |c| assign_contact(c, admin: false) }
def assign_tech_contacts domain.admin_domain_contacts_attributes = @admin_contacts
attrs = [] domain.tech_domain_contacts_attributes = @tech_contacts
params[:tech_domain_contacts_attributes].each do |c|
contact = Contact.find_by(code: c)
domain.add_epp_error('2303', 'contact', c, %i[domain_contacts not_found]) if contact.blank?
attrs << { contact_id: contact.id, contact_code_cache: contact.code } if contact
end
domain.tech_domain_contacts_attributes = attrs
end end
def assign_expiry_time def assign_expiry_time
period = domain.period.to_i period = Integer(domain.period)
plural_period_unit_name = (domain.period_unit == 'm' ? 'months' : 'years').to_sym plural_period_unit_name = (domain.period_unit == 'm' ? 'months' : 'years').to_sym
exp = (Time.zone.now.advance(plural_period_unit_name => period) + 1.day).beginning_of_day exp = (Time.zone.now.advance(plural_period_unit_name => period) + 1.day).beginning_of_day
domain.expire_time = exp domain.expire_time = exp
end end
def debit_registrar def action_billable?
@domain_pricelist ||= domain.pricelist('create', domain.period.try(:to_i), domain.period_unit) unless domain_pricelist&.price
if @domain_pricelist.try(:price) && domain.registrar.balance < @domain_pricelist.price.amount
domain.add_epp_error(2104, nil, nil, I18n.t('billing_failure_credit_balance_low'))
return
elsif !@domain_pricelist.try(:price)
domain.add_epp_error(2104, nil, nil, I18n.t(:active_price_missing_for_this_operation)) domain.add_epp_error(2104, nil, nil, I18n.t(:active_price_missing_for_this_operation))
return return false
end end
domain.registrar.debit!(sum: @domain_pricelist.price.amount, price: @domain_pricelist, if domain.registrar.balance < domain_pricelist.price.amount
domain.add_epp_error(2104, nil, nil, I18n.t('billing_failure_credit_balance_low'))
return false
end
true
end
def debit_registrar
return unless action_billable?
domain.registrar.debit!(sum: domain_pricelist.price.amount, price: domain_pricelist,
description: "#{I18n.t('create')} #{domain.name}", description: "#{I18n.t('create')} #{domain.name}",
activity_type: AccountActivity::CREATE) activity_type: AccountActivity::CREATE)
end end
def process_auction_and_disputes def domain_pricelist
dn = DNS::DomainName.new(SimpleIDN.to_unicode(params[:name])) @domain_pricelist ||= domain.pricelist('create', domain.period.try(:to_i), domain.period_unit)
Dispute.close_by_domain(domain.name)
return unless Domain.release_to_auction && dn.pending_registration?
auction = Auction.find_by(domain: domain.name, status: Auction.statuses[:payment_received]) @domain_pricelist
auction.domain_registered!
end end
def maybe_attach_legal_doc def maybe_attach_legal_doc
Actions::BaseAction.attach_legal_doc_to_new(domain, params[:legal_document], domain: true) Actions::BaseAction.attach_legal_doc_to_new(domain, params[:legal_document], domain: true)
end end
def commit def process_auction_and_disputes
unless domain.valid? dn = DNS::DomainName.new(domain.name)
domain.errors.delete(:name_dirty) if domain.errors[:puny_label].any? Dispute.close_by_domain(domain.name)
return false if domain.errors.any? return unless Domain.release_to_auction && dn.pending_registration?
Auction.find_by(domain: domain.name,
status: Auction.statuses[:payment_received])&.domain_registered!
end end
def commit
return false if validation_process_errored?
debit_registrar debit_registrar
return false if domain.errors.any? return false if domain.errors.any?
process_auction_and_disputes process_auction_and_disputes
domain.save domain.save
end end
def validation_process_errored?
return if domain.valid?
domain.errors.delete(:name_dirty) if domain.errors[:puny_label].any?
domain.errors.any?
end
end end
end end

View file

@ -1,28 +1,30 @@
module Actions module Actions
class DomainUpdate class DomainUpdate # rubocop:disable Metrics/ClassLength
attr_reader :domain, :params, :bypass_verify attr_reader :domain, :params, :bypass_verify
def initialize(domain, params, bypass_verify) def initialize(domain, params, bypass_verify)
@domain = domain @domain = domain
@params = params @params = params
@bypass_verify = bypass_verify @bypass_verify = bypass_verify
@changes_registrant = false
end end
def call def call
@changes_registrant = false
validate_domain_integrity validate_domain_integrity
assign_new_registrant if params[:registrant] assign_new_registrant if params[:registrant]
assign_nameserver_modifications if params[:nameservers] assign_relational_modifications
assign_admin_contact_changes if params[:contacts] assign_requested_statuses
assign_tech_contact_changes if params[:contacts]
assign_requested_statuses if params[:statuses]
assign_dnssec_modifications if params[:dns_keys]
maybe_attach_legal_doc maybe_attach_legal_doc
commit commit
end end
def assign_relational_modifications
assign_nameserver_modifications if params[:nameservers]
assign_dnssec_modifications if params[:dns_keys]
(assign_admin_contact_changes && assign_tech_contact_changes) if params[:contacts]
end
def validate_domain_integrity def validate_domain_integrity
domain.auth_info = params[:auth_info] if params[:auth_info] domain.auth_info = params[:auth_info] if params[:auth_info]
@ -67,7 +69,7 @@ module Actions
end end
def validate_ns_integrity(ns_attr) def validate_ns_integrity(ns_attr)
ns = domain.nameservers.find_by_hash_params(ns_attr.except(:action)).first ns = domain.nameservers.from_hash_params(ns_attr.except(:action)).first
if ns if ns
@nameservers << { id: ns.id, _destroy: 1 } @nameservers << { id: ns.id, _destroy: 1 }
else else
@ -78,16 +80,16 @@ module Actions
def assign_dnssec_modifications def assign_dnssec_modifications
@dnskeys = [] @dnskeys = []
params[:dns_key].each do |key| params[:dns_keys].each do |key|
case key[:action] case key[:action]
when 'add' when 'add'
validate_dnskey_integrity(key) && dnskeys << key.except(:action) validate_dnskey_integrity(key) && @dnskeys << key.except(:action)
when 'rem' when 'rem'
assign_removable_dnskey(key) assign_removable_dnskey(key)
end end
end end
domain.dnskeys_attributes = dnskeys domain.dnskeys_attributes = @dnskeys
end end
def validate_dnskey_integrity(key) def validate_dnskey_integrity(key)
@ -136,7 +138,7 @@ module Actions
contacts.each do |c| contacts.each do |c|
contact = contact_for_action(action: c[:action], method: admin ? 'admin' : 'tech', contact = contact_for_action(action: c[:action], method: admin ? 'admin' : 'tech',
code: c[:code]) code: c[:code])
entry = assign_contact(contact, add: c[:action] == 'add', admin: admin) entry = assign_contact(contact, add: c[:action] == 'add', admin: admin, code: c[:code])
props << entry if entry.is_a?(Hash) props << entry if entry.is_a?(Hash)
end end
@ -150,11 +152,11 @@ module Actions
domain.tech_domain_contacts.find_by(contact_code_cache: code) domain.tech_domain_contacts.find_by(contact_code_cache: code)
end end
def assign_contact(obj, add: false, admin: true) def assign_contact(obj, add: false, admin: true, code:)
if obj.blank? if obj.blank?
domain.add_epp_error('2303', 'contact', obj[:code], %i[domain_contacts not_found]) domain.add_epp_error('2303', 'contact', code, %i[domain_contacts not_found])
elsif obj.org? && admin elsif obj.org? && admin
domain.add_epp_error('2306', 'contact', obj[:code], domain.add_epp_error('2306', 'contact', code,
%i[domain_contacts admin_contact_can_be_only_private_person]) %i[domain_contacts admin_contact_can_be_only_private_person])
else else
add ? { contact_id: obj.id, contact_code_cache: obj.code } : { id: obj.id, _destroy: 1 } add ? { contact_id: obj.id, contact_code_cache: obj.code } : { id: obj.id, _destroy: 1 }
@ -162,70 +164,82 @@ module Actions
end end
def assign_requested_statuses def assign_requested_statuses
rem = [] return unless params[:statuses]
add = []
params[:statuses].each do |s| @rem = []
status, action = s.slice(:status, :action) @add = []
if permitted_status?(status, action) @failed = false
action == 'add' ? add << status : rem << action
end params[:statuses].each { |s| verify_status_eligiblity(s) }
domain.statuses = (domain.statuses - @rem + @add) unless @failed
end end
domain.statuses = domain.statuses - rem + add unless domain.errors.any? def verify_status_eligiblity(status_entry)
status, action = status_entry.select_keys(:status, :action)
return unless permitted_status?(status, action)
action == 'add' ? @add << status : @rem << status
end end
def permitted_status?(status, action) def permitted_status?(status, action)
if DomainStatus::CLIENT_STATUSES.include?(status) && if DomainStatus::CLIENT_STATUSES.include?(status) &&
(domain.statuses.include?(status) || action == 'add') (domain.statuses.include?(status) || action == 'add')
return true
true
else
domain.add_epp_error('2303', 'status', s[:status], %i[statuses not_found])
false
end end
domain.add_epp_error('2303', 'status', status, %i[statuses not_found])
@failed = true
false
end end
def verify_registrant_change? def verify_registrant_change?
return false unless @changes_registrant return if !@changes_registrant || params[:registrant][:verified] == true
return false if params[:registrant][:verified] == true
return true unless domain.disputed? return true unless domain.disputed?
return validate_dispute_case if params[:reserved_pw]
if params[:reserved_pw] domain.add_epp_error('2304', nil, nil, 'Required parameter missing; reservedpw element ' \
'required for dispute domains')
true
end
def validate_dispute_case
dispute = Dispute.active.find_by(domain_name: domain.name, password: params[:reserved_pw]) dispute = Dispute.active.find_by(domain_name: domain.name, password: params[:reserved_pw])
if dispute if dispute
Dispute.close_by_domain(domain.name) Dispute.close_by_domain(domain.name)
return false false
else else
domain.add_epp_error('2202', nil, nil, domain.add_epp_error('2202', nil, nil,
'Invalid authorization information; invalid reserved>pw value') 'Invalid authorization information; invalid reserved>pw value')
end
else
domain.add_epp_error('2304', nil, nil, 'Required parameter missing; reservedpw element ' \
'required for dispute domains')
end
true true
end end
end
def maybe_attach_legal_doc def maybe_attach_legal_doc
Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document]) Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document])
end end
def commit def ask_registrant_verification
return false if domain.errors[:epp_errors].any?
return false unless domain.valid?
if verify_registrant_change? && !bypass_verify && if verify_registrant_change? && !bypass_verify &&
Setting.request_confirmation_on_registrant_change_enabled && !bypass_verify Setting.request_confirmation_on_registrant_change_enabled
domain.registrant_verification_asked!(params, params[:registrar_id]) domain.registrant_verification_asked!(params, params[:registrar_id])
end end
end
return false if domain.errors[:epp_errors].any? def commit
return false unless domain.valid? return false if any_errors?
ask_registrant_verification
return false if any_errors?
domain.save domain.save
end end
def any_errors?
return true if domain.errors[:epp_errors].any? || domain.invalid?
false
end
end end
end end

View file

@ -63,7 +63,7 @@ class Nameserver < ApplicationRecord
end end
class << self class << self
def find_by_hash_params params def from_hash_params params
params = params.with_indifferent_access params = params.with_indifferent_access
rel = all rel = all
rel = rel.where(hostname: params[:hostname]) rel = rel.where(hostname: params[:hostname])

View file

@ -1,4 +1,5 @@
require 'core_monkey_patches/array' require 'core_monkey_patches/array'
require 'core_monkey_patches/hash'
require 'gem_monkey_patches/builder' require 'gem_monkey_patches/builder'
require 'gem_monkey_patches/i18n' require 'gem_monkey_patches/i18n'
require 'gem_monkey_patches/paper_trail' require 'gem_monkey_patches/paper_trail'

View file

@ -0,0 +1,5 @@
class Hash
def select_keys(*args)
select { |k, _| args.include?(k) }.map { |_k, v| v }
end
end

View file

@ -47,14 +47,16 @@ module Deserializers
# schema validation prevents both in the same parent node # schema validation prevents both in the same parent node
if frame.css('dsData').present? if frame.css('dsData').present?
frame.css('dsData').each do |ds_data| frame.css('dsData').each { |k| @ds_data << key_from_params(k, dsa: true) }
@ds_data << Deserializers::Xml::DnssecKey.new(ds_data, true).call
end
else
frame.css('keyData').each do |key|
@key_data << Deserializers::Xml::DnssecKey.new(key, false).call
end end
return if frame.css('keyData').blank?
frame.css('keyData').each { |k| @key_data << key_from_params(k, dsa: false) }
end end
def key_from_params(obj, dsa: false)
Deserializers::Xml::DnssecKey.new(obj, dsa).call
end end
def call def call
@ -67,9 +69,8 @@ module Deserializers
end end
def mark_destroy(dns_keys) def mark_destroy(dns_keys)
(ds_data.present? ? ds_filter(dns_keys) : kd_filter(dns_keys)).map do |inf_data| data = ds_data.present? ? ds_filter(dns_keys) : kd_filter(dns_keys)
inf_data.blank? ? nil : mark(inf_data) data.each { |inf_data| inf_data.blank? ? nil : mark(inf_data) }
end
end end
private private

View file

@ -1,8 +1,7 @@
module Deserializers module Deserializers
module Xml module Xml
class Domain class Domain
attr_reader :frame attr_reader :frame, :registrar
attr_reader :registrar
def initialize(frame, registrar) def initialize(frame, registrar)
@frame = frame @frame = frame
@ -15,15 +14,24 @@ module Deserializers
registrar_id: registrar, registrar_id: registrar,
registrant_id: if_present('registrant'), registrant_id: if_present('registrant'),
reserved_pw: if_present('reserved > pw'), reserved_pw: if_present('reserved > pw'),
period: frame.css('period').text.present? ? Integer(frame.css('period').text) : 1,
period_unit: frame.css('period').first ? frame.css('period').first[:unit] : 'y',
} }
attributes.merge!(assign_period_attributes)
pw = frame.css('authInfo > pw').text pw = frame.css('authInfo > pw').text
attributes[:transfer_code] = pw if pw.present? attributes[:transfer_code] = pw if pw.present?
attributes.compact attributes.compact
end end
def assign_period_attributes
period = frame.css('period')
{
period: period.text.present? ? Integer(period.text) : 1,
period_unit: period.first ? period.first[:unit] : 'y',
}
end
def if_present(css_path) def if_present(css_path)
return if frame.css(css_path).blank? return if frame.css(css_path).blank?

View file

@ -42,20 +42,18 @@ module Deserializers
end end
def nameservers def nameservers
nameservers = [] @nameservers = []
frame.css('add > ns > hostAttr').each do |ns|
nsrv = Deserializers::Xml::Nameserver.new(ns).call frame.css('add > ns > hostAttr').each { |ns| assign_ns(ns) }
nsrv[:action] = 'add' frame.css('rem > ns > hostAttr').each { |ns| assign_ns(ns, add: false) }
nameservers << nsrv
@nameservers.presence
end end
frame.css('rem > ns > hostAttr').each do |ns| def assign_ns(nameserver, add: true)
nsrv = Deserializers::Xml::Nameserver.new(ns).call nsrv = Deserializers::Xml::Nameserver.new(nameserver).call
nsrv[:action] = 'rem' nsrv[:action] = add ? 'add' : 'rem'
nameservers << nsrv @nameservers << nsrv
end
nameservers.presence
end end
def dns_keys def dns_keys
@ -72,17 +70,12 @@ module Deserializers
def statuses def statuses
return if frame.css('status').blank? return if frame.css('status').blank?
statuses = [] s = []
frame.css('add > status').each do |e| frame.css('add > status').each { |e| s << { status: e.attr('s'), action: 'add' } }
statuses << { status: e.attr('s').to_s, action: 'add' } frame.css('rem > status').each { |e| s << { status: e.attr('s'), action: 'rem' } }
end
frame.css('rem > status').each do |e| s
statuses << { status: e.attr('s').to_s, action: 'rem' }
end
statuses
end end
def legal_document def legal_document