Merge branch 'master' into fix-for-ransack-deprecated-method

This commit is contained in:
Dinar Safiulin 2021-09-14 13:50:18 +03:00 committed by GitHub
commit 9cafc98482
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
171 changed files with 1061 additions and 726 deletions

View file

@ -25,13 +25,12 @@ module Admin
@account_activities = @q.result.page(params[:page]).per(params[:results_per_page])
@count = @q.result.count
if params[:page] && params[:page].to_i > 1
@sum = @q.result.limit(@account_activities.offset_value).sum(:sum) +
@b.result.where("account_activities.id NOT IN (#{@q.result.select(:id).to_sql})")
.sum(:sum)
else
@sum = @b.result.where("account_activities.id NOT IN (#{@q.result.select(:id).to_sql})").sum(:sum)
end
@sum = if params[:page] && params[:page].to_i > 1
@q.result.limit(@account_activities.offset_value).sum(:sum) +
@b.result.where("account_activities.id NOT IN (#{@q.result.select(:id).to_sql})").sum(:sum)
else
@b.result.where("account_activities.id NOT IN (#{@q.result.select(:id).to_sql})").sum(:sum)
end
respond_to do |format|
format.html

View file

@ -63,7 +63,12 @@ module Admin
def admin_user_params
params.require(:admin_user).permit(:username,
:password, :password_confirmation, :identity_code, :email, :country_code, { roles: [] })
:password,
:password_confirmation,
:identity_code,
:email,
:country_code,
{ roles: [] })
end
end
end

View file

@ -61,9 +61,9 @@ module Admin
end
def bank_statement_params
params.require(:bank_statement).permit(:bank_code, :iban, bank_transactions_attributes: [
:description, :sum, :currency, :reference_no, :paid_at
])
params.require(:bank_statement).permit(:bank_code, :iban, bank_transactions_attributes: %i[
description sum currency reference_no paid_at
])
end
end
end

View file

@ -43,7 +43,6 @@ module Admin
end
end
def blocked_domain_params
params.require(:blocked_domain).permit(:name)
end

View file

@ -11,20 +11,22 @@ module Admin
@versions = @q.result.page(params[:page])
search_params = params[:q].deep_dup
whereS = "1=1"
where_s = "1=1"
search_params.each do |key, value|
next if value.empty?
case key
when 'event'
whereS += " AND event = '#{value}'"
else
whereS += create_where_string(key, value)
end
where_s += case key
when 'event'
" AND event = '#{value}'"
else
create_where_string(key, value)
end
end
versions = Version::ContactVersion.includes(:item).where(whereS).order(created_at: :desc, id: :desc)
@q = versions.ransack(params[:q])
@versions = @q.result.page(params[:page])
@versions = @versions.per(params[:results_per_page]) if params[:results_per_page].to_i.positive?

View file

@ -9,7 +9,8 @@ module Admin
params[:q] ||= {}
search_params = params[:q].deep_dup
if search_params[:domain_contacts_type_in].is_a?(Array) && search_params[:domain_contacts_type_in].delete('registrant')
if search_params[:domain_contacts_type_in].is_a?(Array) &&
search_params[:domain_contacts_type_in].delete('registrant')
search_params[:registrant_domains_id_not_null] = 1
end

View file

@ -4,4 +4,4 @@ module Admin
def show; end
end
end
end

View file

@ -21,27 +21,33 @@ module Admin
search_params.delete(:registrar)
end
whereS = "1=1"
where_s = "1=1"
search_params.each do |key, value|
next if value.empty?
case key
when 'event'
whereS += " AND event = '#{value}'"
when 'name'
whereS += " AND (object->>'name' ~* '#{value}' OR object_changes->>'name' ~* '#{value}')"
else
whereS += create_where_string(key, value)
end
where_s += case key
when 'event'
" AND event = '#{value}'"
when 'name'
" AND (object->>'name' ~* '#{value}' OR object_changes->>'name' ~* '#{value}')"
else
create_where_string(key, value)
end
end
whereS += " AND object->>'registrant_id' IN (#{registrants.map { |r| "'#{r.id.to_s}'" }.join ','})" if registrants.present?
whereS += " AND 1=0" if registrants == []
whereS += " AND object->>'registrar_id' IN (#{registrars.map { |r| "'#{r.id.to_s}'" }.join ','})" if registrars.present?
whereS += " AND 1=0" if registrars == []
if registrants.present?
where_s += " AND object->>'registrant_id' IN (#{registrants.map { |r| "'#{r.id}'" }.join ','})"
end
where_s += " AND 1=0" if registrants == []
if registrars.present?
where_s += " AND object->>'registrar_id' IN (#{registrars.map { |r| "'#{r.id}'" }.join ','})"
end
where_s += " AND 1=0" if registrars == []
versions = Version::DomainVersion.includes(:item).where(whereS).order(created_at: :desc, id: :desc)
@q = versions.ransack(params[:q])
@versions = @q.result.page(params[:page])
@versions = @versions.per(params[:results_per_page]) if params[:results_per_page].to_i.positive?

View file

@ -5,20 +5,19 @@ module Admin
def index
params[:q] ||= {}
if params[:statuses_contains]
domains = Domain.includes(:registrar, :registrant).where(
"domains.statuses @> ?::varchar[]", "{#{params[:statuses_contains].join(',')}}"
)
else
domains = Domain.includes(:registrar, :registrant)
end
domains = if params[:statuses_contains]
Domain.includes(:registrar, :registrant).where(
"domains.statuses @> ?::varchar[]", "{#{params[:statuses_contains].join(',')}}"
)
else
Domain.includes(:registrar, :registrant)
end
normalize_search_parameters do
@q = domains.ransack(params[:q])
@domains = @q.result.page(params[:page])
if @domains.count == 1 && params[:q][:name_matches].present?
redirect_to [:admin, @domains.first] and return
elsif @domains.count == 0 && params[:q][:name_matches] !~ /^%.+%$/
(redirect_to [:admin, @domains.first] and return if @domains.count == 1 && params[:q][:name_matches].present?)
if @domains.count.zero? && params[:q][:name_matches] !~ /^%.+%$/
# if we do not get any results, add wildcards to the name field and search again
n_cache = params[:q][:name_matches]
params[:q][:name_matches] = "%#{params[:q][:name_matches]}%"
@ -51,7 +50,7 @@ module Admin
redirect_to [:admin, @domain]
else
build_associations
flash.now[:alert] = I18n.t('failed_to_update_domain') + ' ' + @domain.errors.full_messages.join(", ")
flash.now[:alert] = "#{I18n.t('failed_to_update_domain')} #{@domain.errors.full_messages.join(', ')}"
render 'edit'
end
end

View file

@ -3,17 +3,25 @@ module Admin
load_and_authorize_resource class: ApiLog::EppLog
before_action :set_default_dates, only: [:index]
# rubocop:disable Metrics/MethodLength
def index
@q = ApiLog::EppLog.ransack(params[:q])
@q.sorts = 'id desc' if @q.sorts.empty?
@epp_logs = @q.result
@epp_logs = @epp_logs.where("extract(epoch from created_at) >= extract(epoch from ?::timestamp)", Time.parse(params[:q][:created_at_gteq])) if params[:q][:created_at_gteq].present?
@epp_logs = @epp_logs.where("extract(epoch from created_at) <= extract(epoch from ?::timestamp)", Time.parse(params[:q][:created_at_lteq])) if params[:q][:created_at_lteq].present?
if params[:q][:created_at_gteq].present?
@epp_logs = @epp_logs.where("extract(epoch from created_at) >= extract(epoch from ?::timestamp)",
Time.parse(params[:q][:created_at_gteq]))
end
if params[:q][:created_at_lteq].present?
@epp_logs = @epp_logs.where("extract(epoch from created_at) <= extract(epoch from ?::timestamp)",
Time.parse(params[:q][:created_at_lteq]))
end
@epp_logs = @epp_logs.page(params[:page])
render_by_format('admin/epp_logs/index', 'epp_logs')
end
# rubocop:enable Metrics/MethodLength
def show
@epp_log = ApiLog::EppLog.find(params[:id])

View file

@ -10,4 +10,4 @@ module Admin
end
end
end
end
end

View file

@ -3,19 +3,27 @@ module Admin
load_and_authorize_resource class: ApiLog::ReppLog
before_action :set_default_dates, only: [:index]
# rubocop:disable Metrics/MethodLength
def index
@q = ApiLog::ReppLog.ransack(params[:q])
@q.sorts = 'id desc' if @q.sorts.empty?
@repp_logs = @q.result
@repp_logs = @repp_logs.where("extract(epoch from created_at) >= extract(epoch from ?::timestamp)", Time.parse(params[:q][:created_at_gteq])) if params[:q][:created_at_gteq].present?
@repp_logs = @repp_logs.where("extract(epoch from created_at) <= extract(epoch from ?::timestamp)", Time.parse(params[:q][:created_at_lteq])) if params[:q][:created_at_lteq].present?
if params[:q][:created_at_gteq].present?
@repp_logs = @repp_logs.where("extract(epoch from created_at) >= extract(epoch from ?::timestamp)",
Time.parse(params[:q][:created_at_gteq]))
end
if params[:q][:created_at_lteq].present?
@repp_logs = @repp_logs.where("extract(epoch from created_at) <= extract(epoch from ?::timestamp)",
Time.parse(params[:q][:created_at_lteq]))
end
@repp_logs = @repp_logs.page(params[:page])
@count = @q.result.count
@repp_logs = @repp_logs.per(params[:results_per_page]) if paginate?
render_by_format('admin/repp_logs/index', 'repp_logs')
end
# rubocop:enable Metrics/MethodLength
def show
@repp_log = ApiLog::ReppLog.find(params[:id])

View file

@ -14,4 +14,4 @@ module Admin
current_admin_user ? current_admin_user.id_role_username : 'anonymous'
end
end
end
end

View file

@ -45,8 +45,7 @@ module Api
def create_token(user)
token_creator = AuthTokenCreator.create_with_defaults(user)
hash = token_creator.token_in_hash
hash
token_creator.token_in_hash
end
def check_ip_whitelist

View file

@ -15,9 +15,7 @@ module Api
current_registrant: serialized_registrant(@domain.registrant),
}
unless delete_action?
res[:new_registrant] = serialized_registrant(@domain.pending_registrant)
end
res[:new_registrant] = serialized_registrant(@domain.pending_registrant) unless delete_action?
render json: res, status: :ok
end
@ -103,9 +101,10 @@ module Api
end
def verify_action
action = if params[:template] == 'change'
action = case params[:template]
when 'change'
@domain.registrant_update_confirmable?(verify_params[:token])
elsif params[:template] == 'delete'
when 'delete'
@domain.registrant_delete_confirmable?(verify_params[:token])
end

View file

@ -77,9 +77,7 @@ module Api
render json: { errors: [{ address: [error_msg] }] }, status: :bad_request and return
end
if ENV['fax_enabled'] == 'true'
contact.fax = params[:fax] if params[:fax].present?
end
contact.fax = params[:fax] if ENV['fax_enabled'] == 'true' && params[:fax].present?
logger.debug "ENV['fax_enabled'] is set to #{ENV['fax_enabled']}"
if ENV['fax_enabled'] != 'true' && params[:fax]

View file

@ -23,4 +23,4 @@ module Deliverable
def find_invoice
@invoice = Invoice.find(params[:invoice_id])
end
end
end

View file

@ -1,5 +1,5 @@
module Epp
class BaseController < ActionController::Base
class BaseController < ApplicationController
class AuthorizationError < StandardError; end
skip_before_action :verify_authenticity_token
check_authorization
@ -303,16 +303,25 @@ module Epp
if request_command == 'login' && frame.present?
frame.gsub!(/pw>.+<\//, 'pw>[FILTERED]</')
end
trimmed_request = frame.gsub(/<eis:legalDocument([^>]+)>([^<])+<\/eis:legalDocument>/, "<eis:legalDocument>[FILTERED]</eis:legalDocument>") if frame.present?
if frame.present?
trimmed_request = frame.gsub(/<eis:legalDocument([^>]+)>([^<])+<\/eis:legalDocument>/,
"<eis:legalDocument>[FILTERED]</eis:legalDocument>")
end
ApiLog::EppLog.create({
request: trimmed_request,
request_command: request_command,
request_successful: epp_errors.empty?,
request_object: resource ? "#{params[:epp_object_type]}: #{resource.class} - #{resource.id} - #{resource.name}" : params[:epp_object_type],
request_object: if resource
"#{params[:epp_object_type]}: #{resource.class} - "\
"#{resource.id} - #{resource.name}"
else
params[:epp_object_type]
end,
response: @response,
api_user_name: @api_user.try(:username) || current_user.try(:username) || 'api-public',
api_user_registrar: @api_user.try(:registrar).try(:to_s) || current_user.try(:registrar).try(:to_s),
api_user_registrar: @api_user.try(:registrar).try(:to_s) ||
current_user.try(:registrar).try(:to_s),
ip: request.ip,
uuid: request.uuid
})

View file

@ -155,16 +155,78 @@ module Epp
end
def validate_update
if element_count('update > chg > registrant') > 0
requires 'extension > extdata > legalDocument' if current_user.legaldoc_mandatory?
if element_count('update > chg > registrant').positive? && current_user.legaldoc_mandatory?
requires 'extension > extdata > legalDocument'
end
@prefix = 'update > update >'
requires 'name'
dnskey_update_enabled if Feature.obj_and_extensions_statuses_enabled?
dnkey_update_prohibited if Feature.obj_and_extensions_statuses_enabled?
status_editing_disabled
end
def parsed_response_for_dnskey(value)
doc = Nokogiri::Slop params[:parsed_frame].css(value).to_html
return true if doc.document.children.empty?
store = []
if value == 'add'
doc.document.add.children.each_with_index do |x, i|
store << doc.document.add.children[i].name
end
elsif value == 'chg'
doc.document.chg.children.each_with_index do |x, i|
store << doc.document.chg.children[i].name
end
else
doc.document.rem.children.each_with_index do |x, i|
store << doc.document.rem.children[i].name
end
end
return true if store.size == 1 and store[0] == "keyData"
store.empty?
end
def dnskey_update_enabled
find_domain
if @domain.dnskey_update_enabled? && !params[:parsed_frame].css('update').empty?
flag = true
flag = false unless parsed_response_for_dnskey('chg')
if flag
flag = false unless parsed_response_for_dnskey('add')
end
if flag
return if parsed_response_for_dnskey('rem')
end
return epp_errors.add(:epp_errors,
code: '2304',
msg: "#{I18n.t(:object_status_prohibits_operation)}
:serverObjUpdateEnabled")
end
end
def dnkey_update_prohibited
find_domain
if @domain.extension_update_prohibited? && !params[:parsed_frame].css('keyData').empty?
return epp_errors.add(:epp_errors,
code: '2304',
msg: "#{I18n.t(:object_status_prohibits_operation)}
:serverExtensionUpdateProhibited")
end
end
def validate_delete
@prefix = 'delete > delete >'
requires 'name'

View file

@ -51,6 +51,7 @@ module Epp
end
handle_errors(@notification) and return unless @notification.mark_as_read
render_epp_response 'epp/poll/poll_ack'
end

View file

@ -14,38 +14,31 @@ module Epp
webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip)
if webclient_request && !Rails.env.test? && !Rails.env.development?
client_md5 = Certificate.parse_md_from_string(request.env['HTTP_SSL_CLIENT_CERT'])
if ENV['cert_path'].blank?
raise 'webclient cert (cert_path) missing, registrar (r)epp disabled'
end
raise 'webclient cert (cert_path) missing, registrar (r)epp disabled' if ENV['cert_path'].blank?
server_md5 = Certificate.parse_md_from_string(File.read(ENV['cert_path']))
if client_md5 != server_md5
msg = 'Authentication error; server closing connection (certificate is not valid)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
msg: msg, code: '2501')
success = false
end
end
if !Rails.env.development? && (!webclient_request && @api_user)
unless @api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'],
request.env['HTTP_SSL_CLIENT_S_DN_CN'])
msg = 'Authentication error; server closing connection (certificate is not valid)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
if !Rails.env.development? && (!webclient_request && @api_user) &&
!@api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], request.env['HTTP_SSL_CLIENT_S_DN_CN'])
msg = 'Authentication error; server closing connection (certificate is not valid)'
epp_errors.add(:epp_errors,
msg: msg, code: '2501')
success = false
end
success = false
end
if success && !@api_user
msg = 'Authentication error; server closing connection (API user not found)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
msg: msg, code: '2501')
success = false
end
@ -53,8 +46,7 @@ module Epp
if success && !@api_user.try(:active)
msg = 'Authentication error; server closing connection (API user is not active)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
msg: msg, code: '2501')
success = false
end
@ -62,8 +54,7 @@ module Epp
if success && @api_user.cannot?(:create, :epp_login)
msg = 'Authentication error; server closing connection (API user does not have epp role)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
msg: msg, code: '2501')
success = false
end
@ -71,8 +62,7 @@ module Epp
if success && !ip_white?
msg = 'Authentication error; server closing connection (IP is not whitelisted)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2501')
msg: msg, code: '2501')
success = false
end
@ -80,8 +70,7 @@ module Epp
if success && EppSession.limit_reached?(@api_user.registrar)
msg = 'Session limit exceeded; server closing connection (connection limit reached)'
epp_errors.add(:epp_errors,
msg: msg,
code: '2502')
msg: msg, code: '2502')
success = false
end
@ -99,8 +88,7 @@ module Epp
if already_authenticated
epp_errors.add(:epp_errors,
msg: 'Command use error; Already authenticated',
code: 2002)
msg: 'Command use error; Already authenticated', code: 2002)
handle_errors
return
end
@ -118,17 +106,15 @@ module Epp
def ip_white?
webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip)
return true if webclient_request
if @api_user
return false unless @api_user.registrar.api_ip_white?(request.ip)
end
return false if @api_user && !@api_user.registrar.api_ip_white?(request.ip)
true
end
def logout
unless signed_in?
epp_errors.add(:epp_errors,
code: 2201,
msg: 'Authorization error')
code: 2201, msg: 'Authorization error')
handle_errors
return
end

View file

@ -31,4 +31,4 @@ class Registrar
current_registrar_user.registrar.settings['balance_auto_reload']
end
end
end
end

View file

@ -89,7 +89,7 @@ class Registrar
end
def domain_ids_for_bulk_renew
params.dig('domain_ids')&.reject { |id| id.blank? }
params['domain_ids']&.reject { |id| id.blank? }
end
def renew_task(domains)

View file

@ -13,7 +13,8 @@ class Registrar
search_params = params[:q].deep_dup
if search_params[:domain_contacts_type_in].is_a?(Array) && search_params[:domain_contacts_type_in].delete('registrant')
if search_params[:domain_contacts_type_in].is_a?(Array) &&
search_params[:domain_contacts_type_in].delete('registrant')
search_params[:registrant_domains_id_not_null] = 1
end

View file

@ -21,9 +21,7 @@ class Registrar
current_domain_scope
end
if params[:contacts_ident_eq]
domains = domains.where(contacts: { ident: params[:contacts_ident_eq] })
end
domains = domains.where(contacts: { ident: params[:contacts_ident_eq] }) if params[:contacts_ident_eq]
normalize_search_parameters do
@q = domains.ransack(search_params.except(:contacts_ident_eq))

View file

@ -10,4 +10,4 @@ class Registrar
end
end
end
end
end

View file

@ -35,9 +35,7 @@ class Registrar
notices = ["#{t('.replaced')}. #{t('.affected_domains')}: " \
"#{res[:data][:affected_domains].join(', ')}"]
if res[:data][:skipped_domains]
notices << "#{t('.skipped_domains')}: #{res[:data][:skipped_domains].join(', ')}"
end
notices << "#{t('.skipped_domains')}: #{res[:data][:skipped_domains].join(', ')}" if res[:data][:skipped_domains]
notices.join(', ')
end

View file

@ -16,9 +16,7 @@ class Registrar
end
def destroy
@data = depp_current_user.request(@ex.poll(poll: {
value: '', attrs: { op: 'ack', msgID: params[:id] }
}))
@data = depp_current_user.request(@ex.poll(poll: { value: '', attrs: { op: 'ack', msgID: params[:id] } }))
@results = @data.css('result')

View file

@ -30,22 +30,18 @@ class Registrar
show_error and return
end
if @depp_user.pki
unless @api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'],
request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false)
@depp_user.errors.add(:base, :invalid_cert)
end
if @depp_user.pki && !@api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'],
request.env['HTTP_SSL_CLIENT_S_DN_CN'], api: false)
@depp_user.errors.add(:base, :invalid_cert)
end
if @depp_user.errors.none?
if @api_user.active?
sign_in_and_redirect(:registrar_user, @api_user)
else
@depp_user.errors.add(:base, :not_active)
show_error and return
end
show_error and return unless @depp_user.errors.none?
if @api_user.active?
sign_in_and_redirect(:registrar_user, @api_user)
else
show_error and return
@depp_user.errors.add(:base, :not_active)
show_error
end
end
@ -65,9 +61,7 @@ class Registrar
possible_users = ApiUser.where(identity_code: idc) || User.new
possible_users.each do |selected_user|
if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip)
return selected_user
end
return selected_user if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip)
end
end

View file

@ -49,4 +49,4 @@ class Registrar
end
end
end
end
end

View file

@ -22,7 +22,7 @@ class Registrar
def load_xml
cl_trid = "#{depp_current_user.tag}-#{Time.zone.now.to_i}"
xml_dir_path = Rails.root + 'app/views/registrar/xml_consoles/epp_requests'
xml_dir_path = Rails.root.join('app/views/registrar/xml_consoles/epp_requests').to_s
xml = File.read("#{xml_dir_path}/#{params[:obj]}/#{params[:epp_action]}.xml")
xml = prepare_payload(xml, cl_trid)
@ -49,12 +49,12 @@ class Registrar
when 'contact-ee'
insert_prefix_and_version(xml, pref, '1.1')
else
insert_prefix_and_version(xml, pref, '1.1')
insert_prefix_and_version(xml, pref, '1.2')
end
end
def insert_prefix_and_version(xml, pref, version)
xml.gsub!('"' + pref.to_s + '"',
xml.gsub!("\"#{pref}\"",
"\"#{Xsd::Schema.filename(for_prefix: pref.to_s, for_version: version)}\"")
xml
end

View file

@ -74,7 +74,7 @@ module Repp
render_epp_error
end
def render_epp_error(status = :bad_request, data = {})
def render_epp_error(status = :bad_request, **data)
@epp_errors ||= ActiveModel::Errors.new(self)
@epp_errors.add(:epp_errors, msg: 'Command failed', code: '2304') if data != {}

View file

@ -43,7 +43,7 @@ module Repp
return
end
render_success(create_update_success_body)
render_success(**create_update_success_body)
end
api :PUT, '/repp/v1/contacts/:contact_code'
@ -58,7 +58,7 @@ module Repp
return
end
render_success(create_update_success_body)
render_success(**create_update_success_body)
end
api :DELETE, '/repp/v1/contacts/:contact_code'
@ -89,13 +89,11 @@ module Repp
return contacts.pluck(:code) unless details
contacts = contacts.map do |contact|
contacts.map do |contact|
serializer = ::Serializers::Repp::Contact.new(contact,
show_address: Contact.address_processing?)
serializer.to_json
end
contacts
end
def opt_addr?

View file

@ -53,7 +53,7 @@ module Repp
end
def nameserver_params
params.permit(:domain_id, nameservers: [[:hostname, :action, ipv4: [], ipv6: []]])
params.permit(:domain_id, nameservers: [[:hostname, :action, { ipv4: [], ipv6: [] }]])
end
end
end

View file

@ -223,7 +223,7 @@ module Repp
params.require(:domain).permit(:name, :registrant, :period, :period_unit, :registrar,
:transfer_code, :reserved_pw,
dnskeys_attributes: [%i[flags alg protocol public_key]],
nameservers_attributes: [[:hostname, ipv4: [], ipv6: []]],
nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]],
admin_contacts: [], tech_contacts: [])
end
end