mirror of
https://github.com/internetee/registry.git
synced 2025-08-04 17:01:44 +02:00
Merge branch 'master' into monthly-invoices-fix
This commit is contained in:
commit
3ae24f2f68
282 changed files with 2625 additions and 6669 deletions
|
@ -31,12 +31,6 @@ class Ability
|
|||
end
|
||||
|
||||
def epp # Registrar/api_user dynamic role
|
||||
if @user.registrar.api_ip_white?(@ip)
|
||||
can :manage, Depp::Contact
|
||||
can :manage, :xml_console
|
||||
can :manage, Depp::Domain
|
||||
end
|
||||
|
||||
can :manage, Account
|
||||
|
||||
# Poll
|
||||
|
@ -122,7 +116,6 @@ class Ability
|
|||
customer_service
|
||||
can :manage, :registrant_domains
|
||||
can :manage, :registrant_whois
|
||||
can :manage, Depp::Domain
|
||||
can :manage, Domain
|
||||
end
|
||||
|
||||
|
|
|
@ -37,6 +37,24 @@ class Auction < ApplicationRecord
|
|||
find_by(domain: domain_name.to_s, status: PENDING_STATUSES)
|
||||
end
|
||||
|
||||
def self.domain_exists_in_blocked_disputed_and_registered?(domain_name)
|
||||
Domain.exists?(name: domain_name) ||
|
||||
BlockedDomain.exists?(name: domain_name) ||
|
||||
Dispute.exists?(domain_name: domain_name) ||
|
||||
exception_for_registred_or_unbided_existed_auctions(domain_name)
|
||||
end
|
||||
|
||||
def self.exception_for_registred_or_unbided_existed_auctions(domain_name)
|
||||
return false unless Auction.exists?(domain: domain_name)
|
||||
|
||||
auctions = Auction.where(domain: domain_name).order(:created_at)
|
||||
last_record = auctions.last
|
||||
|
||||
return false if last_record.domain_registered? || last_record.no_bids?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def start
|
||||
self.status = self.class.statuses[:started]
|
||||
save!
|
||||
|
|
|
@ -59,8 +59,9 @@ module Invoice::BookKeeping
|
|||
|
||||
duration.times do |dur|
|
||||
single_item_dup = single_item.dup
|
||||
single_item_dup['start_date'] = (issue_date + dur.year).end_of_month.strftime('%Y-%m-%d')
|
||||
single_item_dup['end_date'] = (issue_date + (dur + 1).year).end_of_month.strftime('%Y-%m-%d')
|
||||
date = (issue_date + dur.year).end_of_month.strftime('%Y-%m-%d')
|
||||
single_item_dup['start_date'] = date
|
||||
single_item_dup['end_date'] = date
|
||||
single_item_dup['price'] = (item['price'].to_f / duration).round(2)
|
||||
lines << single_item_dup
|
||||
end
|
||||
|
|
|
@ -38,15 +38,26 @@ module Registrar::BookKeeping
|
|||
lines.as_json
|
||||
end
|
||||
|
||||
def find_or_init_monthly_invoice(month:)
|
||||
def find_or_init_monthly_invoice(month:, overwrite:)
|
||||
invoice = invoices.find_by(monthly_invoice: true, issue_date: month.end_of_month.to_date,
|
||||
cancelled_at: nil)
|
||||
return invoice if invoice
|
||||
return invoice if invoice && !overwrite
|
||||
|
||||
summary = monthly_summary(month: month)
|
||||
return unless summary
|
||||
|
||||
init_monthly_invoice(summary)
|
||||
new_invoice = init_monthly_invoice(summary)
|
||||
return overwrite_invoice(invoice, new_invoice) if invoice && overwrite
|
||||
|
||||
new_invoice
|
||||
end
|
||||
|
||||
def overwrite_invoice(original_invoice, new_invoice)
|
||||
params_to_scrub = %i[created_at updated_at id number sent_at
|
||||
e_invoice_sent_at in_directo cancelled_at payment_link]
|
||||
attrs = new_invoice.attributes.with_indifferent_access.except(*params_to_scrub)
|
||||
original_invoice.update(attrs)
|
||||
original_invoice
|
||||
end
|
||||
|
||||
def title_for_summary(date)
|
||||
|
|
|
@ -32,27 +32,40 @@ module Zone::WhoisQueryable
|
|||
# Take note - since this concern only used to zone whois queries, dnssec keys are set to
|
||||
# empty array
|
||||
def domain_vars
|
||||
{ disclaimer: Setting.registry_whois_disclaimer, name: origin,
|
||||
{
|
||||
disclaimer: Setting.registry_whois_disclaimer, name: origin,
|
||||
registered: created_at.try(:to_s, :iso8601), status: ['ok (paid and in zone)'],
|
||||
changed: updated_at.try(:to_s, :iso8601), email: Setting.registry_email,
|
||||
admin_contacts: [contact_vars], tech_contacts: [contact_vars],
|
||||
nameservers: nameserver_vars, dnssec_keys: [], dnssec_changed: nil }
|
||||
nameservers: nameserver_vars, dnssec_keys: [],
|
||||
dnssec_changed: nil
|
||||
}
|
||||
end
|
||||
|
||||
def registrar_vars
|
||||
{ registrar: Setting.registry_juridical_name, registrar_website: Setting.registry_url,
|
||||
registrar_phone: Setting.registry_phone }
|
||||
{
|
||||
registrar: Setting.registry_juridical_name,
|
||||
registrar_website: Setting.registry_url,
|
||||
registrar_phone: Setting.registry_phone,
|
||||
}
|
||||
end
|
||||
|
||||
def registrant_vars
|
||||
{ registrant: Setting.registry_juridical_name, registrant_reg_no: Setting.registry_reg_no,
|
||||
registrant_ident_country_code: Setting.registry_country_code, registrant_kind: 'org',
|
||||
registrant_disclosed_attributes: %w[name email] }
|
||||
{
|
||||
registrant: Setting.registry_juridical_name,
|
||||
registrant_reg_no: Setting.registry_reg_no,
|
||||
registrant_ident_country_code: Setting.registry_country_code,
|
||||
registrant_kind: 'org',
|
||||
registrant_disclosed_attributes: %w[name email phone],
|
||||
}
|
||||
end
|
||||
|
||||
def contact_vars
|
||||
{ name: Setting.registry_invoice_contact, email: Setting.registry_email,
|
||||
disclosed_attributes: %w[name email] }
|
||||
{
|
||||
name: Setting.registry_invoice_contact,
|
||||
email: Setting.registry_email,
|
||||
disclosed_attributes: %w[name email],
|
||||
}
|
||||
end
|
||||
|
||||
def nameserver_vars
|
||||
|
|
|
@ -64,6 +64,8 @@ class Contact < ApplicationRecord
|
|||
|
||||
validate :validate_html
|
||||
validate :validate_country_code, if: -> { self.class.address_processing? }
|
||||
validates :registrant_publishable, inclusion: { in: [true, false] }, if: -> { registrant? }
|
||||
# validates :registrant_publishable, inclusion: { in: [false] }, unless: -> { registrant? }
|
||||
|
||||
after_initialize do
|
||||
self.status_notes = {} if status_notes.nil?
|
||||
|
@ -145,6 +147,19 @@ class Contact < ApplicationRecord
|
|||
# "clientDeleteProhibited" or "serverDeleteProhibited" status.
|
||||
PENDING_DELETE = 'pendingDelete'.freeze
|
||||
|
||||
DISCLOSE_ATTRIBUTES = %w[
|
||||
name
|
||||
email
|
||||
phone
|
||||
registrant_publishable
|
||||
address
|
||||
fax
|
||||
].freeze
|
||||
|
||||
OPEN_LEGAL_ATTRIBUTES = %w[
|
||||
phone
|
||||
].freeze
|
||||
|
||||
STATUSES = [
|
||||
CLIENT_DELETE_PROHIBITED, SERVER_DELETE_PROHIBITED,
|
||||
CLIENT_TRANSFER_PROHIBITED,
|
||||
|
|
|
@ -15,15 +15,6 @@ class ContactRequest < ApplicationRecord
|
|||
attr_readonly :secret,
|
||||
:valid_to
|
||||
|
||||
def self.save_record(params)
|
||||
contact_request = new(params)
|
||||
contact_request.secret = create_random_secret
|
||||
contact_request.valid_to = set_valid_to_24_hours_from_now
|
||||
contact_request.status = STATUS_NEW
|
||||
contact_request.save!
|
||||
contact_request
|
||||
end
|
||||
|
||||
def update_record(params)
|
||||
self.status = params['status'] if params['status']
|
||||
self.ip_address = params['ip'] if params['ip']
|
||||
|
@ -31,11 +22,22 @@ class ContactRequest < ApplicationRecord
|
|||
save!
|
||||
end
|
||||
|
||||
def self.create_random_secret
|
||||
SecureRandom.hex(64)
|
||||
end
|
||||
class << self
|
||||
def save_record(params)
|
||||
contact_request = new(params)
|
||||
contact_request.secret = create_random_secret
|
||||
contact_request.valid_to = set_valid_to_24_hours_from_now
|
||||
contact_request.status = STATUS_NEW
|
||||
contact_request.save!
|
||||
contact_request
|
||||
end
|
||||
|
||||
def self.set_valid_to_24_hours_from_now
|
||||
(Time.zone.now + 24.hours)
|
||||
def create_random_secret
|
||||
SecureRandom.hex(64)
|
||||
end
|
||||
|
||||
def set_valid_to_24_hours_from_now
|
||||
(Time.zone.now + 24.hours)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,318 +0,0 @@
|
|||
module Depp
|
||||
class Contact
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :id, :name, :email, :phone, :org_name,
|
||||
:ident, :ident_type, :ident_country_code,
|
||||
:street, :city, :zip, :state, :country_code,
|
||||
:password, :legal_document, :statuses, :code,
|
||||
:email_history
|
||||
|
||||
DISABLED = 'Disabled'
|
||||
DISCLOSURE_TYPES = [DISABLED, '1', '0']
|
||||
TYPES = %w( org priv birthday )
|
||||
SELECTION_TYPES = [
|
||||
['Business code', 'org'],
|
||||
['Personal identification code', 'priv'],
|
||||
['Birthday', 'birthday']
|
||||
]
|
||||
|
||||
validates :phone, e164: true, phone: true
|
||||
|
||||
class << self
|
||||
attr_reader :epp_xml, :user
|
||||
|
||||
def new_from_params(params)
|
||||
new(
|
||||
id: params[:code],
|
||||
code: params[:code],
|
||||
email: params[:email],
|
||||
phone: params[:phone],
|
||||
ident: params[:ident],
|
||||
ident_type: params[:ident_type],
|
||||
ident_country_code: params[:ident_country_code],
|
||||
|
||||
# postalInfo
|
||||
name: params[:name],
|
||||
org_name: params[:org_name],
|
||||
|
||||
# address
|
||||
street: params[:street],
|
||||
city: params[:city],
|
||||
zip: params[:zip],
|
||||
state: params[:state],
|
||||
country_code: params[:country_code]
|
||||
)
|
||||
end
|
||||
|
||||
def find_by_id(id)
|
||||
data = info_xml(id)
|
||||
|
||||
res = data.css('epp response resData infData')
|
||||
ext = data.css('epp response extension')
|
||||
new(
|
||||
id: res.css('id').text,
|
||||
code: res.css('id').text,
|
||||
email: res.css('email').text,
|
||||
phone: res.css('voice').text,
|
||||
ident: ext.css('ident').text,
|
||||
ident_type: ext.css('ident').first.try(:attributes).try(:[], 'type').try(:value),
|
||||
ident_country_code: ext.css('ident').first.try(:attributes).try(:[], 'cc').try(:value),
|
||||
|
||||
# postalInfo
|
||||
name: res.css('postalInfo name').text,
|
||||
org_name: res.css('postalInfo org').text,
|
||||
|
||||
# address
|
||||
street: res.css('postalInfo addr street').text,
|
||||
city: res.css('postalInfo addr city').text,
|
||||
zip: res.css('postalInfo addr pc').text,
|
||||
state: res.css('postalInfo addr sp').text,
|
||||
country_code: res.css('postalInfo addr cc').text,
|
||||
|
||||
# authInfo
|
||||
password: res.css('authInfo pw').text,
|
||||
|
||||
# statuses
|
||||
statuses: data.css('status').map { |s| [s['s'], s.text] }
|
||||
)
|
||||
end
|
||||
|
||||
def user=(user)
|
||||
@user = user
|
||||
@epp_xml = EppXml::Contact.new(cl_trid_prefix: user.tag, schema_prefix: 'contact-ee',
|
||||
schema_version: '1.1')
|
||||
end
|
||||
|
||||
def info_xml(id, password = nil)
|
||||
xml = epp_xml.info(
|
||||
id: { value: id },
|
||||
authInfo: { pw: { value: password } }
|
||||
)
|
||||
user.request(xml)
|
||||
end
|
||||
|
||||
def construct_check_hash_from_data(data)
|
||||
res = data.css('epp response resData chkData cd')
|
||||
@contacts = []
|
||||
res.each do |_r|
|
||||
id = res.css('id').try(:text)
|
||||
reason = res.css('reason').present? ? res.css('reason').text : I18n.t(:available)
|
||||
@contacts << { id: id, reason: reason }
|
||||
end
|
||||
@contacts
|
||||
end
|
||||
|
||||
def contact_id_from_xml(data)
|
||||
id = data.css('epp response resData creData id').text
|
||||
id.blank? ? nil : id
|
||||
end
|
||||
|
||||
def construct_create_disclosure_xml(cph, flag)
|
||||
xml = { disclose: {} }
|
||||
cph.each do |k, v|
|
||||
xml[:disclose][k] = {}
|
||||
xml[:disclose][k][:value] = v
|
||||
end
|
||||
xml[:disclose][:attrs] = {}
|
||||
xml[:disclose][:attrs][:flag] = flag
|
||||
xml.with_indifferent_access
|
||||
end
|
||||
|
||||
# cpd = contact_params[:disclose]
|
||||
def extract_disclosure_hash(cpd)
|
||||
return {} unless cpd
|
||||
|
||||
cpd.delete_if { |k, v| v if v != '1' && k == 'flag' }
|
||||
end
|
||||
|
||||
def extract_info_disclosure(data)
|
||||
hash = {}
|
||||
data.css('disclose').each do |d|
|
||||
flag = d.attributes['flag'].value
|
||||
next unless flag
|
||||
hash[flag] = {}
|
||||
d.children.each do |c|
|
||||
hash[flag][c.name] = flag if %w( name email fax voice addr org_name ).include?(c.name)
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
def type_string(type_code)
|
||||
return '' if type_code.blank?
|
||||
t = SELECTION_TYPES.select { |tp| tp.second == type_code }
|
||||
t.try(:first).try(:first)
|
||||
end
|
||||
end
|
||||
|
||||
def save
|
||||
return false unless valid?
|
||||
|
||||
hash = {
|
||||
id: { value: code },
|
||||
postalInfo: {
|
||||
name: { value: name },
|
||||
org: { value: org_name },
|
||||
},
|
||||
voice: { value: phone },
|
||||
email: { value: email }
|
||||
}
|
||||
|
||||
if ::Contact.address_processing?
|
||||
hash[:postalInfo][:addr] = {
|
||||
street: { value: street },
|
||||
city: { value: city },
|
||||
sp: { value: state },
|
||||
pc: { value: zip },
|
||||
cc: { value: country_code },
|
||||
}
|
||||
end
|
||||
|
||||
hash[:id] = nil if code.blank?
|
||||
create_xml = Depp::Contact.epp_xml.create(hash, extension_xml(:create))
|
||||
data = Depp::Contact.user.request(create_xml)
|
||||
self.id = data.css('id').text
|
||||
handle_errors(data)
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def update_attributes(params)
|
||||
return false unless valid?
|
||||
|
||||
self.ident_country_code = params[:ident_country_code]
|
||||
self.ident_type = params[:ident_type]
|
||||
self.ident = params[:ident]
|
||||
|
||||
self.name = params[:name]
|
||||
self.email = params[:email]
|
||||
self.phone = params[:phone]
|
||||
|
||||
self.org_name = params[:org_name]
|
||||
|
||||
if ::Contact.address_processing?
|
||||
self.street = params[:street]
|
||||
self.city = params[:city]
|
||||
self.zip = params[:zip]
|
||||
self.state = params[:state]
|
||||
self.country_code = params[:country_code]
|
||||
end
|
||||
|
||||
attributes = {
|
||||
id: { value: id },
|
||||
chg: {
|
||||
postalInfo: {
|
||||
name: { value: name },
|
||||
org: { value: org_name },
|
||||
},
|
||||
voice: { value: phone },
|
||||
email: { value: email },
|
||||
authInfo: {
|
||||
pw: { value: password }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ::Contact.address_processing?
|
||||
attributes[:chg][:postalInfo][:addr] = {
|
||||
street: { value: street },
|
||||
city: { value: city },
|
||||
sp: { value: state },
|
||||
pc: { value: zip },
|
||||
cc: { value: country_code }
|
||||
}
|
||||
end
|
||||
|
||||
update_xml = Depp::Contact.epp_xml.update(attributes, extension_xml(:update))
|
||||
data = Depp::Contact.user.request(update_xml)
|
||||
handle_errors(data)
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def delete
|
||||
delete_xml = Contact.epp_xml.delete(
|
||||
{
|
||||
id: { value: id },
|
||||
authInfo: { pw: { value: password } }
|
||||
},
|
||||
extension_xml(:delete)
|
||||
)
|
||||
data = Depp::Contact.user.request(delete_xml)
|
||||
handle_errors(data)
|
||||
end
|
||||
|
||||
def extension_xml(action)
|
||||
xml = { _anonymus: [] }
|
||||
|
||||
case action
|
||||
when :create
|
||||
ident = ident_xml[:_anonymus].try(:first)
|
||||
when :update
|
||||
# detect if any ident has changed, nb! ident and self.ident is not always same
|
||||
unless ident == self.ident && ident == ident_type && ident_country_code == self.ident_country_code
|
||||
ident = ident_xml[:_anonymus].try(:first)
|
||||
end
|
||||
end
|
||||
|
||||
legal = legal_document_xml[:_anonymus].try(:first)
|
||||
xml[:_anonymus] << ident if ident.present?
|
||||
xml[:_anonymus] << legal if legal.present?
|
||||
xml
|
||||
end
|
||||
|
||||
def ident_xml
|
||||
{
|
||||
_anonymus: [
|
||||
ident: { value: ident, attrs: { type: ident_type, cc: ident_country_code } }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def legal_document_xml
|
||||
return {} if legal_document.blank?
|
||||
|
||||
type = legal_document.original_filename.split('.').last.downcase
|
||||
{
|
||||
_anonymus: [
|
||||
legalDocument: { value: Base64.encode64(legal_document.read), attrs: { type: type } }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def check(id)
|
||||
xml = epp_xml.check(id: { value: id })
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def country_name
|
||||
Country.new(country_code) || 'No access'
|
||||
end
|
||||
|
||||
def org?
|
||||
ident_type == 'org'
|
||||
end
|
||||
|
||||
def priv?
|
||||
ident_type == 'priv'
|
||||
end
|
||||
|
||||
def persisted?
|
||||
id.present?
|
||||
end
|
||||
|
||||
def handle_errors(data)
|
||||
data.css('result').each do |x|
|
||||
success_codes = %(1000, 1300, 1301)
|
||||
next if success_codes.include?(x['code'])
|
||||
|
||||
message = "#{x.css('msg').text} #{x.css('value').text}"
|
||||
attr = message.split('[').last.strip.sub(']', '') if message.include?('[')
|
||||
attr = :base if attr.nil?
|
||||
attr = 'phone' if attr == 'voice'
|
||||
attr = 'zip' if attr == 'pc'
|
||||
errors.add(attr, message)
|
||||
end
|
||||
errors.blank?
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
module Depp
|
||||
class Dnskey
|
||||
FLAGS = [
|
||||
['0 - not for DNSSEC validation', 0],
|
||||
['256 - ZSK', 256],
|
||||
['257 - KSK', 257]
|
||||
]
|
||||
|
||||
ALGORITHMS = [
|
||||
['3 - DSA/SHA-1', 3],
|
||||
['5 - RSA/SHA-1', 5],
|
||||
['6 - DSA-NSEC3-SHA1', 6],
|
||||
['7 - RSASHA1-NSEC3-SHA1', 7],
|
||||
['8 - RSA/SHA-256', 8],
|
||||
['10 - RSA/SHA-512', 10],
|
||||
['13 - ECDSA Curve P-256 with SHA-256', 13],
|
||||
['14 - ECDSA Curve P-384 with SHA-384', 14],
|
||||
['15 - Ed25519', 15],
|
||||
['16 - Ed448', 16],
|
||||
].freeze
|
||||
|
||||
PROTOCOLS = [3]
|
||||
|
||||
DS_DIGEST_TYPES = [1, 2]
|
||||
end
|
||||
end
|
|
@ -1,351 +0,0 @@
|
|||
module Depp
|
||||
class Domain
|
||||
include ActiveModel::Conversion
|
||||
include RemoveHold
|
||||
extend ActiveModel::Naming
|
||||
|
||||
attr_accessor :name, :current_user, :epp_xml
|
||||
|
||||
STATUSES = %w[
|
||||
clientDeleteProhibited
|
||||
clientHold
|
||||
clientRenewProhibited
|
||||
clientTransferProhibited
|
||||
clientUpdateProhibited
|
||||
].freeze
|
||||
|
||||
PERIODS = [
|
||||
['3 months', '3m'],
|
||||
['6 months', '6m'],
|
||||
['9 months', '9m'],
|
||||
['1 year', '1y'],
|
||||
['2 years', '2y'],
|
||||
['3 years', '3y'],
|
||||
['4 years', '4y'],
|
||||
['5 years', '5y'],
|
||||
['6 years', '6y'],
|
||||
['7 years', '7y'],
|
||||
['8 years', '8y'],
|
||||
['9 years', '9y'],
|
||||
['10 years', '10y'],
|
||||
].freeze
|
||||
|
||||
def initialize(args = {})
|
||||
self.current_user = args[:current_user]
|
||||
self.epp_xml = EppXml::Domain.new(
|
||||
cl_trid_prefix: current_user.tag,
|
||||
schema_prefix: 'domain-ee',
|
||||
schema_version: '1.1'
|
||||
)
|
||||
end
|
||||
|
||||
def info(domain_name)
|
||||
xml = epp_xml.info(name: { value: domain_name })
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def check(domain_name)
|
||||
xml = epp_xml.check(
|
||||
_anonymus: [
|
||||
name: { value: domain_name }
|
||||
]
|
||||
)
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def create(domain_params)
|
||||
dns_hash = {}
|
||||
keys = Domain.create_dnskeys_hash(domain_params)
|
||||
dns_hash[:_anonymus] = keys if keys.any?
|
||||
|
||||
period = domain_params[:period].to_i.to_s
|
||||
period_unit = domain_params[:period][-1].to_s
|
||||
|
||||
xml = if domain_params[:nameservers_attributes]
|
||||
.select { |_key, value| value['hostname'].present? }.any?
|
||||
epp_xml.create({
|
||||
name: { value: domain_params[:name] },
|
||||
period: { value: period, attrs: { unit: period_unit } },
|
||||
ns: Domain.create_nameservers_hash(domain_params),
|
||||
registrant: { value: domain_params[:registrant] },
|
||||
_anonymus: Domain.create_contacts_hash(domain_params)
|
||||
}, dns_hash, Domain.construct_custom_params_hash(domain_params))
|
||||
else
|
||||
epp_xml.create({
|
||||
name: { value: domain_params[:name] },
|
||||
period: { value: period, attrs: { unit: period_unit } },
|
||||
registrant: { value: domain_params[:registrant] },
|
||||
_anonymus: Domain.create_contacts_hash(domain_params)
|
||||
}, dns_hash, Domain.construct_custom_params_hash(domain_params))
|
||||
end
|
||||
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def update(domain_params)
|
||||
data = current_user.request(epp_xml.info(name: { value: domain_params[:name] }))
|
||||
old_domain_params = Depp::Domain.construct_params_from_server_data(data)
|
||||
|
||||
xml = epp_xml.update(
|
||||
Depp::Domain.construct_edit_hash(domain_params, old_domain_params),
|
||||
Depp::Domain.construct_ext_edit_hash(domain_params, old_domain_params),
|
||||
Depp::Domain.construct_custom_params_hash(domain_params)
|
||||
)
|
||||
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def delete(domain_params)
|
||||
xml = epp_xml.delete({
|
||||
name: { value: domain_params[:name] },
|
||||
},
|
||||
Depp::Domain.construct_custom_params_hash(domain_params),
|
||||
(domain_params[:verified].present? && 'yes'))
|
||||
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
def renew(params)
|
||||
period = params[:period].to_i.to_s
|
||||
period_unit = params[:period][-1].to_s
|
||||
|
||||
current_user.request(epp_xml.renew(name: { value: params[:domain_name] },
|
||||
curExpDate: { value: params[:cur_exp_date] },
|
||||
period: { value: period, attrs: { unit: period_unit } }))
|
||||
end
|
||||
|
||||
def transfer(params)
|
||||
op = params[:request] ? 'request' : nil
|
||||
op = params[:query] ? 'query' : op
|
||||
op = params[:approve] ? 'approve' : op
|
||||
op = params[:reject] ? 'reject' : op
|
||||
|
||||
current_user.request(epp_xml.transfer({
|
||||
name: { value: params[:domain_name] },
|
||||
authInfo: { pw: { value: params[:transfer_code] } }
|
||||
}, op, Domain.construct_custom_params_hash(params)))
|
||||
end
|
||||
|
||||
def confirm_transfer(domain_params)
|
||||
data = current_user.request(epp_xml.info(name: { value: domain_params[:name] }))
|
||||
pw = data.css('pw').text
|
||||
|
||||
xml = epp_xml.transfer({
|
||||
name: { value: domain_params[:name] },
|
||||
authInfo: { pw: { value: pw } }
|
||||
}, 'approve')
|
||||
|
||||
current_user.request(xml)
|
||||
end
|
||||
|
||||
class << self
|
||||
def default_period
|
||||
'1y'
|
||||
end
|
||||
|
||||
def default_params
|
||||
ret = {}
|
||||
|
||||
ret[:contacts_attributes] ||= {}
|
||||
ENV['default_admin_contacts_count'].to_i.times do |i|
|
||||
ret[:contacts_attributes][i] = { code: '', type: 'admin' }
|
||||
end
|
||||
|
||||
ret[:nameservers_attributes] ||= {}
|
||||
ENV['default_nameservers_count'].to_i.times do |i|
|
||||
ret[:nameservers_attributes][i] = {}
|
||||
end
|
||||
|
||||
ret[:dnskeys_attributes] ||= { 0 => {} }
|
||||
ret[:statuses_attributes] ||= { 0 => {} }
|
||||
ret.with_indifferent_access
|
||||
end
|
||||
|
||||
def construct_params_from_server_data(data)
|
||||
ret = default_params
|
||||
ret[:name] = data.css('name').text
|
||||
ret[:registrant] = data.css('registrant').text
|
||||
|
||||
data.css('contact').each_with_index do |x, i|
|
||||
ret[:contacts_attributes][i] = { code: x.text, type: x['type'] }
|
||||
end
|
||||
|
||||
data.css('hostAttr').each_with_index do |x, i|
|
||||
ret[:nameservers_attributes][i] = {
|
||||
hostname: x.css('hostName').text,
|
||||
ipv4: Array(x.css('hostAddr[ip="v4"]')).map(&:text).join(','),
|
||||
ipv6: Array(x.css('hostAddr[ip="v6"]')).map(&:text).join(',')
|
||||
}
|
||||
end
|
||||
|
||||
data.css('keyData').each_with_index do |x, i|
|
||||
ret[:dnskeys_attributes][i] = {
|
||||
flags: x.css('flags').text,
|
||||
protocol: x.css('protocol').text,
|
||||
alg: x.css('alg').text,
|
||||
public_key: x.css('pubKey').text,
|
||||
ds_key_tag: x.css('keyTag').first.try(:text),
|
||||
ds_alg: x.css('alg').first.try(:text),
|
||||
ds_digest_type: x.css('digestType').first.try(:text),
|
||||
ds_digest: x.css('digest').first.try(:text)
|
||||
}
|
||||
end
|
||||
|
||||
data.css('status').each_with_index do |x, i|
|
||||
next unless STATUSES.include?(x['s'])
|
||||
|
||||
ret[:statuses_attributes][i] = {
|
||||
code: x['s'],
|
||||
description: x.text
|
||||
}
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def construct_custom_params_hash(domain_params)
|
||||
custom_params = { _anonymus: [] }
|
||||
if domain_params[:legal_document].present?
|
||||
type = domain_params[:legal_document].original_filename.split('.').last.downcase
|
||||
custom_params[:_anonymus] << {
|
||||
legalDocument: { value: Base64.encode64(domain_params[:legal_document].read), attrs: { type: type } }
|
||||
}
|
||||
end
|
||||
|
||||
if domain_params[:reserved_pw].present?
|
||||
custom_params[:_anonymus] << { reserved: { pw: { value: domain_params[:reserved_pw] } } }
|
||||
end
|
||||
|
||||
custom_params
|
||||
end
|
||||
|
||||
def construct_edit_hash(domain_params, old_domain_params)
|
||||
contacts = array_difference(create_contacts_hash(domain_params), create_contacts_hash(old_domain_params))
|
||||
add_anon = contacts
|
||||
|
||||
contacts = array_difference(create_contacts_hash(old_domain_params), create_contacts_hash(domain_params))
|
||||
rem_anon = contacts
|
||||
|
||||
add_arr = []
|
||||
add_ns = create_nameservers_hash(domain_params) - create_nameservers_hash(old_domain_params)
|
||||
add_arr << { ns: add_ns } if add_ns.any?
|
||||
add_arr << { _anonymus: add_anon } if add_anon.any?
|
||||
|
||||
rem_arr = []
|
||||
rem_ns = create_nameservers_hash(old_domain_params) - create_nameservers_hash(domain_params)
|
||||
rem_arr << { ns: rem_ns } if rem_ns.any?
|
||||
rem_arr << { _anonymus: rem_anon } if rem_anon.any?
|
||||
|
||||
if domain_params[:registrant] != old_domain_params[:registrant]
|
||||
chg = [{ registrant: { value: domain_params[:registrant] } }] unless domain_params[:verified].present?
|
||||
if domain_params[:verified]
|
||||
chg = [{ registrant: { value: domain_params[:registrant], attrs: { verified: 'yes' } } }]
|
||||
end
|
||||
end
|
||||
|
||||
add_arr = nil if add_arr.none?
|
||||
rem_arr = nil if rem_arr.none?
|
||||
|
||||
{
|
||||
name: { value: domain_params[:name] },
|
||||
add: add_arr,
|
||||
rem: rem_arr,
|
||||
chg: chg
|
||||
}
|
||||
end
|
||||
|
||||
def construct_ext_edit_hash(domain_params, old_domain_params)
|
||||
rem_keys = create_dnskeys_hash(old_domain_params) - create_dnskeys_hash(domain_params)
|
||||
add_keys = create_dnskeys_hash(domain_params) - create_dnskeys_hash(old_domain_params)
|
||||
hash = {}
|
||||
hash[:rem] = rem_keys if rem_keys.any?
|
||||
hash[:add] = add_keys if add_keys.any?
|
||||
hash
|
||||
end
|
||||
|
||||
def create_nameservers_hash(domain_params)
|
||||
ret = []
|
||||
domain_params[:nameservers_attributes].each do |_k, v|
|
||||
next if v['hostname'].blank?
|
||||
|
||||
host_attr = []
|
||||
host_attr << { hostName: { value: v['hostname'] } }
|
||||
if v['ipv4'].present?
|
||||
v['ipv4'].to_s.split(',').each do |ip|
|
||||
host_attr << { hostAddr: { value: ip, attrs: { ip: 'v4' } } }
|
||||
end
|
||||
end
|
||||
|
||||
if v['ipv6'].present?
|
||||
v['ipv6'].to_s.split(',').each do |ip|
|
||||
host_attr << { hostAddr: { value: ip, attrs: { ip: 'v6' } } }
|
||||
end
|
||||
end
|
||||
|
||||
ret << { hostAttr: host_attr }
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def create_contacts_hash(domain_params)
|
||||
ret = []
|
||||
domain_params[:contacts_attributes].each do |_k, v|
|
||||
next if v['code'].blank?
|
||||
|
||||
ret << {
|
||||
contact: { value: v['code'], attrs: { type: v['type'] } }
|
||||
}
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def create_dnskeys_hash(domain_params)
|
||||
ret = []
|
||||
domain_params[:dnskeys_attributes].each do |_k, v|
|
||||
if v['ds_key_tag'].blank?
|
||||
kd = create_key_data_hash(v)
|
||||
if kd
|
||||
ret << {
|
||||
keyData: kd
|
||||
}
|
||||
end
|
||||
else
|
||||
ret << {
|
||||
dsData: [
|
||||
keyTag: { value: v['ds_key_tag'] },
|
||||
alg: { value: v['ds_alg'] },
|
||||
digestType: { value: v['ds_digest_type'] },
|
||||
digest: { value: v['ds_digest'] },
|
||||
keyData: create_key_data_hash(v)
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def create_key_data_hash(key_data_params)
|
||||
return nil if key_data_params['public_key'].blank?
|
||||
|
||||
{
|
||||
flags: { value: key_data_params['flags'] },
|
||||
protocol: { value: key_data_params['protocol'] },
|
||||
alg: { value: key_data_params['alg'] },
|
||||
pubKey: { value: key_data_params['public_key'] }
|
||||
}
|
||||
end
|
||||
|
||||
def array_difference(x, y)
|
||||
ret = x.dup
|
||||
y.each do |element|
|
||||
index = ret.index(element)
|
||||
ret.delete_at(index) if index
|
||||
end
|
||||
ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
module Depp
|
||||
class User
|
||||
include ActiveModel::Validations
|
||||
include ActiveModel::Conversion
|
||||
extend ActiveModel::Naming
|
||||
|
||||
attr_accessor :tag, :password, :pki
|
||||
|
||||
validates :tag, :password, presence: true
|
||||
|
||||
validate :validate_existance_in_server
|
||||
|
||||
def initialize(args = {})
|
||||
args.each { |k, v| send("#{k}=", v) }
|
||||
end
|
||||
|
||||
def server
|
||||
client_cert = File.read(ENV['cert_path'])
|
||||
client_key = File.read(ENV['key_path'])
|
||||
port = ENV['epp_port'] || '700'
|
||||
|
||||
@server_cache ||= Epp::Server.new({
|
||||
server: ENV['epp_hostname'],
|
||||
tag: tag,
|
||||
password: password,
|
||||
port: port,
|
||||
cert: OpenSSL::X509::Certificate.new(client_cert),
|
||||
key: OpenSSL::PKey::RSA.new(client_key)
|
||||
})
|
||||
end
|
||||
|
||||
def request(xml)
|
||||
Nokogiri::XML(server.request(xml)).remove_namespaces!
|
||||
rescue EppErrorResponse => e
|
||||
Nokogiri::XML(e.response_xml.to_s).remove_namespaces!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_existance_in_server
|
||||
return if errors.any?
|
||||
res = server.open_connection
|
||||
unless Nokogiri::XML(res).css('greeting')
|
||||
errors.add(:base, :failed_to_open_connection_to_epp_server)
|
||||
server.close_connection # just in case
|
||||
return
|
||||
end
|
||||
|
||||
ex = EppXml::Session.new(cl_trid_prefix: tag)
|
||||
xml = ex.login(clID: { value: tag }, pw: { value: password })
|
||||
res = server.send_request(xml)
|
||||
|
||||
if Nokogiri::XML(res).css('result').first['code'] != '1000'
|
||||
errors.add(:base, Nokogiri::XML(res).css('result').text)
|
||||
end
|
||||
|
||||
server.close_connection
|
||||
|
||||
rescue OpenSSL::SSL::SSLError => e
|
||||
Rails.logger.error "INVALID CERT: #{e}"
|
||||
Rails.logger.error "INVALID CERT DEBUG INFO: epp_hostname: #{ENV['epp_hostname']}," \
|
||||
"port: #{ENV['epp_port']}, cert_path: #{ENV['cert_path']}, key_path: #{ENV['key_path']}"
|
||||
errors.add(:base, :invalid_cert)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,10 +25,10 @@ class Dnskey < ApplicationRecord
|
|||
}
|
||||
|
||||
# IANA numbers, single authority list
|
||||
ALGORITHMS = Depp::Dnskey::ALGORITHMS.map {|pair| pair[1].to_s}.freeze
|
||||
PROTOCOLS = %w(3)
|
||||
FLAGS = %w(0 256 257) # 256 = ZSK, 257 = KSK
|
||||
DS_DIGEST_TYPE = [1,2]
|
||||
ALGORITHMS = %w[3 5 6 7 8 10 13 14 15 16].freeze
|
||||
PROTOCOLS = %w[3].freeze
|
||||
FLAGS = %w[0 256 257].freeze # 256 = ZSK, 257 = KSK
|
||||
DS_DIGEST_TYPE = [1, 2].freeze
|
||||
RESOLVERS = ENV['dnssec_resolver_ips'].to_s.strip.split(', ').freeze
|
||||
self.ignored_columns = %w[legacy_domain_id]
|
||||
|
||||
|
|
|
@ -13,6 +13,22 @@ class Domain < ApplicationRecord
|
|||
include Domain::Disputable
|
||||
include Domain::BulkUpdatable
|
||||
|
||||
PERIODS = [
|
||||
['3 months', '3m'],
|
||||
['6 months', '6m'],
|
||||
['9 months', '9m'],
|
||||
['1 year', '1y'],
|
||||
['2 years', '2y'],
|
||||
['3 years', '3y'],
|
||||
['4 years', '4y'],
|
||||
['5 years', '5y'],
|
||||
['6 years', '6y'],
|
||||
['7 years', '7y'],
|
||||
['8 years', '8y'],
|
||||
['9 years', '9y'],
|
||||
['10 years', '10y'],
|
||||
].freeze
|
||||
|
||||
attr_accessor :roles,
|
||||
:legal_document_id,
|
||||
:is_admin,
|
||||
|
@ -49,7 +65,6 @@ class Domain < ApplicationRecord
|
|||
statuses.include? DomainStatus::SERVER_REGISTRANT_CHANGE_PROHIBITED
|
||||
end
|
||||
|
||||
|
||||
# NB! contacts, admin_contacts, tech_contacts are empty for a new record
|
||||
has_many :domain_contacts, dependent: :destroy
|
||||
has_many :contacts, through: :domain_contacts, source: :contact
|
||||
|
|
|
@ -105,6 +105,9 @@ class Epp::Domain < Domain
|
|||
max: Setting.ns_max_count
|
||||
}
|
||||
],
|
||||
'2502' => [ # Rate limit exceeded
|
||||
%i[base session_limit_exceeded],
|
||||
],
|
||||
]
|
||||
}
|
||||
end
|
||||
|
|
|
@ -62,7 +62,7 @@ module Epp
|
|||
2308 => 'Data management policy violation',
|
||||
2400 => 'Command failed',
|
||||
2501 => 'Authentication error; server closing connection',
|
||||
2502 => 'Session limit exceeded; server closing connection',
|
||||
2502 => Shunter.default_error_message,
|
||||
}.freeze
|
||||
private_constant :DEFAULT_DESCRIPTIONS
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class LegalDocument < ApplicationRecord
|
||||
class LegalDocument < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
||||
include EppErrors
|
||||
MIN_BODY_SIZE = (1.37 * 3.kilobytes).ceil
|
||||
MAX_BODY_SIZE = 8.megabytes
|
||||
|
@ -14,7 +14,7 @@ class LegalDocument < ApplicationRecord
|
|||
|
||||
belongs_to :documentable, polymorphic: true
|
||||
|
||||
validate :val_body_length, if: ->(file) { file.path.blank? }
|
||||
validate :val_body_length, if: ->(file) { file.path.blank? && (Rails.env.production? || Rails.env.test?) }
|
||||
|
||||
before_create :add_creator
|
||||
before_save :save_to_filesystem, if: :body
|
||||
|
@ -24,7 +24,7 @@ class LegalDocument < ApplicationRecord
|
|||
'2308' => [
|
||||
%i[body length_more_than],
|
||||
%i[body length_less_than],
|
||||
]
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -41,12 +41,13 @@ class LegalDocument < ApplicationRecord
|
|||
digest = Digest::SHA1.new.update(binary).to_s
|
||||
|
||||
loop do
|
||||
rand = SecureRandom.random_number.to_s.last(4)
|
||||
next if rand.to_i == 0 || rand.length < 4
|
||||
dir = "#{ENV['legal_documents_dir']}/#{Time.zone.now.strftime('%Y/%m/%d')}"
|
||||
FileUtils.mkdir_p(dir, mode: 0775)
|
||||
self.path = "#{dir}/#{Time.zone.now.to_formatted_s(:number)}_#{rand}.#{document_type}"
|
||||
break unless File.file?(path)
|
||||
rand = SecureRandom.random_number.to_s.last(4)
|
||||
next if rand.to_i.zero? || rand.length < 4
|
||||
|
||||
dir = "#{ENV['legal_documents_dir']}/#{Time.zone.now.strftime('%Y/%m/%d')}"
|
||||
FileUtils.mkdir_p(dir, mode: 0775)
|
||||
self.path = "#{dir}/#{Time.zone.now.to_formatted_s(:number)}_#{rand}.#{document_type}"
|
||||
break unless File.file?(path)
|
||||
end
|
||||
|
||||
File.open(path, 'wb') { |f| f.write(binary) } unless Rails.env.test?
|
||||
|
@ -69,50 +70,57 @@ class LegalDocument < ApplicationRecord
|
|||
start = Time.zone.now.to_f
|
||||
Rails.logger.info '-----> Removing legal documents duplicates'
|
||||
count = 0
|
||||
modified = Array.new
|
||||
modified = []
|
||||
|
||||
LegalDocument.where(documentable_type: "Domain").where.not(checksum: [nil, ""]).find_each do |orig_legal|
|
||||
LegalDocument.where(documentable_type: 'Domain')
|
||||
.where.not(checksum: [nil, ''])
|
||||
.find_each do |orig_legal|
|
||||
next if modified.include?(orig_legal.checksum)
|
||||
next if !File.exist?(orig_legal.path)
|
||||
next unless File.exist?(orig_legal.path)
|
||||
|
||||
modified.push(orig_legal.checksum)
|
||||
|
||||
LegalDocument.where(documentable_type: "Domain", documentable_id: orig_legal.documentable_id).
|
||||
where(checksum: orig_legal.checksum).
|
||||
where.not(id: orig_legal.id).where.not(path: orig_legal.path).each do |new_legal|
|
||||
unless modified.include?(orig_legal.id)
|
||||
File.delete(new_legal.path) if File.exist?(new_legal.path)
|
||||
new_legal.update(path: orig_legal.path)
|
||||
count += 1
|
||||
Rails.logger.info "File #{new_legal.path} has been removed by Domain "\
|
||||
"#{new_legal.documentable_id}. Document id: #{new_legal.id}"
|
||||
end
|
||||
LegalDocument.where(documentable_type: 'Domain', documentable_id: orig_legal.documentable_id)
|
||||
.where(checksum: orig_legal.checksum)
|
||||
.where.not(id: orig_legal.id)
|
||||
.where.not(path: orig_legal.path).each do |new_legal|
|
||||
next if modified.include?(orig_legal.id)
|
||||
|
||||
File.delete(new_legal.path) if File.exist?(new_legal.path)
|
||||
new_legal.update(path: orig_legal.path)
|
||||
count += 1
|
||||
Rails.logger.info "File #{new_legal.path} has been removed by Domain "\
|
||||
"#{new_legal.documentable_id}. Document id: #{new_legal.id}"
|
||||
end
|
||||
|
||||
contact_ids = Version::DomainVersion.where(item_id: orig_legal.documentable_id).distinct.
|
||||
pluck("object->>'registrant_id'", "object_changes->>'registrant_id'",
|
||||
"children->>'tech_contacts'", "children->>'admin_contacts'").flatten.uniq
|
||||
contact_ids = contact_ids.map{|id|
|
||||
contact_ids = Version::DomainVersion.where(item_id: orig_legal.documentable_id).distinct
|
||||
.pluck("object->>'registrant_id'",
|
||||
"object_changes->>'registrant_id'",
|
||||
"children->>'tech_contacts'",
|
||||
"children->>'admin_contacts'")
|
||||
.flatten.uniq
|
||||
contact_ids = contact_ids.map do |id|
|
||||
case id
|
||||
when Hash
|
||||
id["id"]
|
||||
when String
|
||||
JSON.parse(id) rescue id.to_i
|
||||
else
|
||||
id
|
||||
end
|
||||
}.flatten.compact.uniq
|
||||
LegalDocument.where(documentable_type: "Contact", documentable_id: contact_ids).
|
||||
where(checksum: orig_legal.checksum).where.not(path: orig_legal.path).each do |new_legal|
|
||||
unless modified.include?(orig_legal.id)
|
||||
File.delete(new_legal.path) if File.exist?(new_legal.path)
|
||||
new_legal.update(path: orig_legal.path)
|
||||
count += 1
|
||||
Rails.logger.info "File #{new_legal.path} has been removed by Contact "\
|
||||
"#{new_legal.documentable_id}. Document id: #{new_legal.id}"
|
||||
when Hash
|
||||
id['id']
|
||||
when String
|
||||
JSON.parse(id) rescue id.to_i
|
||||
else
|
||||
id
|
||||
end
|
||||
end.flatten.compact.uniq
|
||||
LegalDocument.where(documentable_type: 'Contact', documentable_id: contact_ids)
|
||||
.where(checksum: orig_legal.checksum)
|
||||
.where.not(path: orig_legal.path).each do |new_legal|
|
||||
next if modified.include?(orig_legal.id)
|
||||
|
||||
File.delete(new_legal.path) if File.exist?(new_legal.path)
|
||||
new_legal.update(path: orig_legal.path)
|
||||
count += 1
|
||||
Rails.logger.info "File #{new_legal.path} has been removed by Contact "\
|
||||
"#{new_legal.documentable_id}. Document id: #{new_legal.id}"
|
||||
end
|
||||
end
|
||||
Rails.logger.info "-----> Duplicates fixed for #{count} rows in #{(Time.zone.now.to_f - start).round(2)} seconds"
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,4 +3,8 @@ class Registrant < Contact
|
|||
def epp_code_map
|
||||
{}
|
||||
end
|
||||
|
||||
def publishable?
|
||||
registrant_publishable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ class ValidationEvent < ApplicationRecord
|
|||
smtp: 1,
|
||||
}.freeze
|
||||
|
||||
store_accessor :event_data, :errors, :check_level, :email
|
||||
store_accessor :event_data, :check_level, :email
|
||||
|
||||
belongs_to :validation_eventable, polymorphic: true
|
||||
|
||||
|
|
|
@ -51,7 +51,9 @@ class WhoisRecord < ApplicationRecord
|
|||
end
|
||||
|
||||
h[:email] = registrant.email
|
||||
h[:registrant_changed] = registrant.updated_at.try(:to_s, :iso8601)
|
||||
h[:phone] = registrant.phone
|
||||
h[:registrant_publishable] = registrant.publishable?
|
||||
h[:registrant_changed] = registrant.updated_at.try(:to_s, :iso8601)
|
||||
h[:registrant_disclosed_attributes] = registrant.disclosed_attributes
|
||||
|
||||
h[:admin_contacts] = []
|
||||
|
@ -78,7 +80,6 @@ class WhoisRecord < ApplicationRecord
|
|||
h[:dnssec_keys] = domain.dnskeys.map { |key| "#{key.flags} #{key.protocol} #{key.alg} #{key.public_key}" }
|
||||
h[:dnssec_changed] = domain.dnskeys.pluck(:updated_at).max.try(:to_s, :iso8601) rescue nil
|
||||
|
||||
|
||||
h
|
||||
end
|
||||
|
||||
|
@ -112,8 +113,10 @@ class WhoisRecord < ApplicationRecord
|
|||
{
|
||||
name: contact.name,
|
||||
email: contact.email,
|
||||
phone: contact.phone,
|
||||
changed: contact.updated_at.try(:to_s, :iso8601),
|
||||
disclosed_attributes: contact.disclosed_attributes,
|
||||
contact_publishable: contact.registrant_publishable?,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue