mirror of
https://github.com/internetee/registry.git
synced 2025-06-10 22:54:47 +02:00
Merge branch '269-dispute-list' of https://github.com/internetee/registry into 269-dispute-list
This commit is contained in:
commit
cb18d1dbb1
17 changed files with 136 additions and 44 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
11.05.2020
|
||||||
|
* Auction process due dates are now available over whois and rest-whois [#1201](https://github.com/internetee/registry/issues/1201)
|
||||||
|
|
||||||
|
30.04.2020
|
||||||
|
* Fix for internal error on opening domain history with legacy id record [#1576](https://github.com/internetee/registry/issues/1576)
|
||||||
|
|
||||||
27.04.2020
|
27.04.2020
|
||||||
* Downgrade SimpleCov to 0.17 due to incompatibiilty with CodeClimate [#1575](https://github.com/internetee/registry/pull/1575)
|
* Downgrade SimpleCov to 0.17 due to incompatibiilty with CodeClimate [#1575](https://github.com/internetee/registry/pull/1575)
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,8 @@ module Repp
|
||||||
webclient_cert_name = ENV['webclient_cert_common_name'] || 'webclient'
|
webclient_cert_name = ENV['webclient_cert_common_name'] || 'webclient'
|
||||||
error! "Webclient #{message} #{webclient_cert_name}", 401 if webclient_cert_name != request_name
|
error! "Webclient #{message} #{webclient_cert_name}", 401 if webclient_cert_name != request_name
|
||||||
else
|
else
|
||||||
unless @current_user.api_pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], request.env['HTTP_SSL_CLIENT_S_DN_CN'])
|
unless @current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'],
|
||||||
|
request.env['HTTP_SSL_CLIENT_S_DN_CN'])
|
||||||
error! "#{message} #{@current_user.username}", 401
|
error! "#{message} #{@current_user.username}", 401
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,8 @@ module Api
|
||||||
raise "Invalid status #{params[:status]}"
|
raise "Invalid status #{params[:status]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
auction.mark_deadline(params[:registration_deadline]) if params[:registration_deadline]
|
||||||
|
|
||||||
if auction.payment_not_received? || auction.domain_not_registered?
|
if auction.payment_not_received? || auction.domain_not_registered?
|
||||||
update_whois_from_auction(Auction.pending(auction.domain))
|
update_whois_from_auction(Auction.pending(auction.domain))
|
||||||
else
|
else
|
||||||
|
|
|
@ -26,7 +26,8 @@ module Epp
|
||||||
end
|
end
|
||||||
|
|
||||||
if !Rails.env.development? && (!webclient_request && @api_user)
|
if !Rails.env.development? && (!webclient_request && @api_user)
|
||||||
unless @api_user.api_pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], request.env['HTTP_SSL_CLIENT_S_DN_CN'])
|
unless @api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'],
|
||||||
|
request.env['HTTP_SSL_CLIENT_S_DN_CN'])
|
||||||
epp_errors << {
|
epp_errors << {
|
||||||
msg: 'Authentication error; server closing connection (certificate is not valid)',
|
msg: 'Authentication error; server closing connection (certificate is not valid)',
|
||||||
code: '2501'
|
code: '2501'
|
||||||
|
|
|
@ -31,7 +31,8 @@ class Registrar
|
||||||
end
|
end
|
||||||
|
|
||||||
if @depp_user.pki
|
if @depp_user.pki
|
||||||
unless @api_user.registrar_pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], request.env['HTTP_SSL_CLIENT_S_DN_CN'])
|
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)
|
@depp_user.errors.add(:base, :invalid_cert)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,24 +64,14 @@ class ApiUser < User
|
||||||
registrar.notifications.unread
|
registrar.notifications.unread
|
||||||
end
|
end
|
||||||
|
|
||||||
def registrar_pki_ok?(crt, cn)
|
def pki_ok?(crt, com, api: true)
|
||||||
return false if crt.blank? || cn.blank?
|
return false if crt.blank? || com.blank?
|
||||||
crt = crt.split(' ').join("\n")
|
|
||||||
crt.gsub!("-----BEGIN\nCERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\n")
|
|
||||||
crt.gsub!("\n-----END\nCERTIFICATE-----", "\n-----END CERTIFICATE-----")
|
|
||||||
cert = OpenSSL::X509::Certificate.new(crt)
|
|
||||||
md5 = OpenSSL::Digest::MD5.new(cert.to_der).to_s
|
|
||||||
certificates.registrar.exists?(md5: md5, common_name: cn)
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_pki_ok?(crt, cn)
|
origin = api ? certificates.api : certificates.registrar
|
||||||
return false if crt.blank? || cn.blank?
|
cert = machine_readable_certificate(crt)
|
||||||
crt = crt.split(' ').join("\n")
|
|
||||||
crt.gsub!("-----BEGIN\nCERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\n")
|
|
||||||
crt.gsub!("\n-----END\nCERTIFICATE-----", "\n-----END CERTIFICATE-----")
|
|
||||||
cert = OpenSSL::X509::Certificate.new(crt)
|
|
||||||
md5 = OpenSSL::Digest::MD5.new(cert.to_der).to_s
|
md5 = OpenSSL::Digest::MD5.new(cert.to_der).to_s
|
||||||
certificates.api.exists?(md5: md5, common_name: cn)
|
|
||||||
|
origin.exists?(md5: md5, common_name: com, revoked: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def linked_users
|
def linked_users
|
||||||
|
@ -93,4 +83,14 @@ class ApiUser < User
|
||||||
def linked_with?(another_api_user)
|
def linked_with?(another_api_user)
|
||||||
another_api_user.identity_code == self.identity_code
|
another_api_user.identity_code == self.identity_code
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def machine_readable_certificate(cert)
|
||||||
|
cert = cert.split(' ').join("\n")
|
||||||
|
cert.gsub!("-----BEGIN\nCERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\n")
|
||||||
|
cert.gsub!("\n-----END\nCERTIFICATE-----", "\n-----END CERTIFICATE-----")
|
||||||
|
|
||||||
|
OpenSSL::X509::Certificate.new(cert)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,10 +23,19 @@ class Auction < ApplicationRecord
|
||||||
save!
|
save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def whois_deadline
|
||||||
|
registration_deadline.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def mark_as_no_bids
|
def mark_as_no_bids
|
||||||
no_bids!
|
no_bids!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mark_deadline(registration_deadline)
|
||||||
|
self.registration_deadline = registration_deadline
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
|
||||||
def mark_as_payment_received
|
def mark_as_payment_received
|
||||||
self.status = self.class.statuses[:payment_received]
|
self.status = self.class.statuses[:payment_received]
|
||||||
generate_registration_code
|
generate_registration_code
|
||||||
|
|
|
@ -32,20 +32,21 @@ class Certificate < ApplicationRecord
|
||||||
errors.add(:base, I18n.t(:invalid_csr_or_crt))
|
errors.add(:base, I18n.t(:invalid_csr_or_crt))
|
||||||
end
|
end
|
||||||
|
|
||||||
before_create :parse_metadata
|
validate :assign_metadata, on: :create
|
||||||
def parse_metadata
|
|
||||||
if crt
|
def assign_metadata
|
||||||
pc = parsed_crt.try(:subject).try(:to_s) || ''
|
origin = crt ? parsed_crt : parsed_csr
|
||||||
cn = pc.scan(/\/CN=(.+)/).flatten.first
|
parse_metadata(origin)
|
||||||
self.common_name = cn.split('/').first
|
rescue NoMethodError
|
||||||
self.md5 = OpenSSL::Digest::MD5.new(parsed_crt.to_der).to_s
|
errors.add(:base, I18n.t(:invalid_csr_or_crt))
|
||||||
self.interface = API
|
|
||||||
elsif csr
|
|
||||||
pc = parsed_csr.try(:subject).try(:to_s) || ''
|
|
||||||
cn = pc.scan(/\/CN=(.+)/).flatten.first
|
|
||||||
self.common_name = cn.split('/').first
|
|
||||||
self.interface = REGISTRAR
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_metadata(origin)
|
||||||
|
pc = origin.subject.to_s
|
||||||
|
cn = pc.scan(%r{\/CN=(.+)}).flatten.first
|
||||||
|
self.common_name = cn.split('/').first
|
||||||
|
self.md5 = OpenSSL::Digest::MD5.new(origin.to_der).to_s if crt
|
||||||
|
self.interface = crt ? API : REGISTRAR
|
||||||
end
|
end
|
||||||
|
|
||||||
def parsed_crt
|
def parsed_crt
|
||||||
|
@ -116,6 +117,7 @@ class Certificate < ApplicationRecord
|
||||||
-revoke #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch")
|
-revoke #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch")
|
||||||
|
|
||||||
if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/)
|
if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/)
|
||||||
|
self.revoked = true
|
||||||
save!
|
save!
|
||||||
@cached_status = REVOKED
|
@cached_status = REVOKED
|
||||||
else
|
else
|
||||||
|
|
|
@ -70,7 +70,8 @@ module Versions
|
||||||
valid_columns = ver.item_type.constantize&.column_names
|
valid_columns = ver.item_type.constantize&.column_names
|
||||||
o = new(ver.object&.slice(*valid_columns))
|
o = new(ver.object&.slice(*valid_columns))
|
||||||
o.version_loader = ver
|
o.version_loader = ver
|
||||||
ver.object_changes.to_h.each { |k, v| o.public_send("#{k}=", v[-1]) }
|
changes = ver.object_changes.to_h&.slice(*valid_columns)
|
||||||
|
changes.each { |k, v| o.public_send("#{k}=", v[-1]) }
|
||||||
o
|
o
|
||||||
end
|
end
|
||||||
not_in_history = where(id: (ids.to_a - from_history.map(&:id)))
|
not_in_history = where(id: (ids.to_a - from_history.map(&:id)))
|
||||||
|
|
|
@ -16,7 +16,8 @@ module Whois
|
||||||
elsif auction.awaiting_payment? || auction.payment_received?
|
elsif auction.awaiting_payment? || auction.payment_received?
|
||||||
update!(json: { name: auction.domain,
|
update!(json: { name: auction.domain,
|
||||||
status: ['PendingRegistration'],
|
status: ['PendingRegistration'],
|
||||||
disclaimer: self.class.disclaimer })
|
disclaimer: self.class.disclaimer,
|
||||||
|
registration_deadline: auction.whois_deadline })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddRegistrationDeadlineDateToModels < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :auctions, :registration_deadline, :datetime
|
||||||
|
end
|
||||||
|
end
|
5
db/migrate/20200505103316_add_revoked_to_certificate.rb
Normal file
5
db/migrate/20200505103316_add_revoked_to_certificate.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class AddRevokedToCertificate < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :certificates, :revoked, :boolean, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -325,7 +325,8 @@ CREATE TABLE public.auctions (
|
||||||
status character varying NOT NULL,
|
status character varying NOT NULL,
|
||||||
uuid uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
uuid uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||||
created_at timestamp without time zone NOT NULL,
|
created_at timestamp without time zone NOT NULL,
|
||||||
registration_code character varying
|
registration_code character varying,
|
||||||
|
registration_deadline timestamp without time zone
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -476,7 +477,8 @@ CREATE TABLE public.certificates (
|
||||||
updated_at timestamp without time zone,
|
updated_at timestamp without time zone,
|
||||||
common_name character varying,
|
common_name character varying,
|
||||||
md5 character varying,
|
md5 character varying,
|
||||||
interface character varying
|
interface character varying,
|
||||||
|
revoked boolean DEFAULT false NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -4515,6 +4517,8 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||||
('20200203143458'),
|
('20200203143458'),
|
||||||
('20200204103125'),
|
('20200204103125'),
|
||||||
('20200311114649'),
|
('20200311114649'),
|
||||||
|
('20200417075720'),
|
||||||
('20200421093637'),
|
('20200421093637'),
|
||||||
|
('20200505103316'),
|
||||||
('20200505150413');
|
('20200505150413');
|
||||||
|
|
||||||
|
|
14
test/fixtures/certificates.yml
vendored
Normal file
14
test/fixtures/certificates.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
api:
|
||||||
|
api_user: api_bestnames
|
||||||
|
common_name: registry.test
|
||||||
|
crt: "-----BEGIN CERTIFICATE-----\nMIICYjCCAcugAwIBAgIBADANBgkqhkiG9w0BAQ0FADBNMQswCQYDVQQGEwJ1czEO\nMAwGA1UECAwFVGV4YXMxFjAUBgNVBAoMDVJlZ2lzdHJ5IHRlc3QxFjAUBgNVBAMM\nDXJlZ2lzdHJ5LnRlc3QwIBcNMjAwNTA1MTIzNzQxWhgPMjEyMDA0MTExMjM3NDFa\nME0xCzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVUZXhhczEWMBQGA1UECgwNUmVnaXN0\ncnkgdGVzdDEWMBQGA1UEAwwNcmVnaXN0cnkudGVzdDCBnzANBgkqhkiG9w0BAQEF\nAAOBjQAwgYkCgYEAyn+GCkUJIhdXVBOPrZH+Zj2B/tQfL5TLZwVYZQt38x6GQT+4\n6ndty467IJvKSUlHej7uMpsCzC8Ffmda4cZm16jO1vUb4hXIrmeKP84zLrrUpKag\ngZR4rBDbG2+uL4SzMyy3yeQysYuTiQ4N1i4vdhvkKYPSWIht/QFvuzdFq+0CAwEA\nAaNQME4wHQYDVR0OBBYEFD6B5j6NnMCDBnfbtjBYKBJM7sCRMB8GA1UdIwQYMBaA\nFD6B5j6NnMCDBnfbtjBYKBJM7sCRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEN\nBQADgYEArtCR6VOabD3nM/KlZTmHMZVT4ntenYlNTM9FS0RatzPmdh4REhykvmZs\nOlBcpoV5tN5Y8bHOVRqY9V2e903QEhQgoccQhbt0Py6uFwfLv+WLKAUbeGnPqK9d\ndL3wXN9BQs0hJA6IZNFyz2F/gSTURrD1zWW2na3ipRzhupW5+98=\n-----END CERTIFICATE-----\n"
|
||||||
|
md5: e6771ed5dc857a1dbcc1e0a36baa1fee
|
||||||
|
interface: api
|
||||||
|
revoked: false
|
||||||
|
registrar:
|
||||||
|
api_user: api_bestnames
|
||||||
|
common_name: registry.test
|
||||||
|
crt: "-----BEGIN CERTIFICATE-----\nMIICYjCCAcugAwIBAgIBADANBgkqhkiG9w0BAQ0FADBNMQswCQYDVQQGEwJ1czEO\nMAwGA1UECAwFVGV4YXMxFjAUBgNVBAoMDVJlZ2lzdHJ5IHRlc3QxFjAUBgNVBAMM\nDXJlZ2lzdHJ5LnRlc3QwIBcNMjAwNTA1MTIzNzQxWhgPMjEyMDA0MTExMjM3NDFa\nME0xCzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVUZXhhczEWMBQGA1UECgwNUmVnaXN0\ncnkgdGVzdDEWMBQGA1UEAwwNcmVnaXN0cnkudGVzdDCBnzANBgkqhkiG9w0BAQEF\nAAOBjQAwgYkCgYEAyn+GCkUJIhdXVBOPrZH+Zj2B/tQfL5TLZwVYZQt38x6GQT+4\n6ndty467IJvKSUlHej7uMpsCzC8Ffmda4cZm16jO1vUb4hXIrmeKP84zLrrUpKag\ngZR4rBDbG2+uL4SzMyy3yeQysYuTiQ4N1i4vdhvkKYPSWIht/QFvuzdFq+0CAwEA\nAaNQME4wHQYDVR0OBBYEFD6B5j6NnMCDBnfbtjBYKBJM7sCRMB8GA1UdIwQYMBaA\nFD6B5j6NnMCDBnfbtjBYKBJM7sCRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEN\nBQADgYEArtCR6VOabD3nM/KlZTmHMZVT4ntenYlNTM9FS0RatzPmdh4REhykvmZs\nOlBcpoV5tN5Y8bHOVRqY9V2e903QEhQgoccQhbt0Py6uFwfLv+WLKAUbeGnPqK9d\ndL3wXN9BQs0hJA6IZNFyz2F/gSTURrD1zWW2na3ipRzhupW5+98=\n-----END CERTIFICATE-----\n"
|
||||||
|
md5: e6771ed5dc857a1dbcc1e0a36baa1fee
|
||||||
|
interface: registrar
|
||||||
|
revoked: false
|
|
@ -39,6 +39,17 @@ class ApiV1AuctionUpdateTest < ActionDispatch::IntegrationTest
|
||||||
assert @auction.awaiting_payment?
|
assert @auction.awaiting_payment?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_sets_registration_deadline
|
||||||
|
deadline = (Time.zone.now + 10.days).end_of_day
|
||||||
|
patch api_v1_auction_path(@auction.uuid),
|
||||||
|
params: { status: Auction.statuses[:awaiting_payment],
|
||||||
|
registration_deadline: deadline},
|
||||||
|
as: :json
|
||||||
|
@auction.reload
|
||||||
|
|
||||||
|
assert_in_delta @auction.registration_deadline, deadline, 1.second
|
||||||
|
end
|
||||||
|
|
||||||
def test_marks_as_no_bids
|
def test_marks_as_no_bids
|
||||||
patch api_v1_auction_path(@auction.uuid),
|
patch api_v1_auction_path(@auction.uuid),
|
||||||
params: { status: Auction.statuses[:no_bids] },
|
params: { status: Auction.statuses[:no_bids] },
|
||||||
|
|
|
@ -63,6 +63,25 @@ class ApiUserTest < ActiveSupport::TestCase
|
||||||
assert_nil ApiUser.find_by_id_card(id_card)
|
assert_nil ApiUser.find_by_id_card(id_card)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_verifies_pki_status
|
||||||
|
certificate = certificates(:api)
|
||||||
|
|
||||||
|
assert @user.pki_ok?(certificate.crt, certificate.common_name, api: true)
|
||||||
|
assert_not @user.pki_ok?(certificate.crt, 'invalid-cn', api: true)
|
||||||
|
|
||||||
|
certificate = certificates(:registrar)
|
||||||
|
|
||||||
|
assert @user.pki_ok?(certificate.crt, certificate.common_name, api: false)
|
||||||
|
assert_not @user.pki_ok?(certificate.crt, 'invalid-cn', api: false)
|
||||||
|
|
||||||
|
certificate.update(revoked: true)
|
||||||
|
assert_not @user.pki_ok?(certificate.crt, certificate.common_name, api: false)
|
||||||
|
|
||||||
|
certificate = certificates(:api)
|
||||||
|
certificate.update(revoked: true)
|
||||||
|
assert_not @user.pki_ok?(certificate.crt, certificate.common_name, api: true)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def valid_user
|
def valid_user
|
||||||
|
|
|
@ -40,24 +40,34 @@ class Whois::RecordTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_updates_whois_record_from_auction_when_awaiting_payment
|
def test_updates_whois_record_from_auction_when_awaiting_payment
|
||||||
@auction.update!(domain: 'domain.test', status: Auction.statuses[:awaiting_payment])
|
@auction.update!(domain: 'domain.test',
|
||||||
|
status: Auction.statuses[:awaiting_payment],
|
||||||
|
registration_deadline: registration_deadline)
|
||||||
@whois_record.update!(name: 'domain.test')
|
@whois_record.update!(name: 'domain.test')
|
||||||
@whois_record.update_from_auction(@auction)
|
@whois_record.update_from_auction(@auction)
|
||||||
@whois_record.reload
|
@whois_record.reload
|
||||||
|
|
||||||
assert_equal ({ 'name' => 'domain.test',
|
assert_equal ({ 'name' => 'domain.test',
|
||||||
'status' => ['PendingRegistration'],
|
'status' => ['PendingRegistration'],
|
||||||
'disclaimer' => 'disclaimer' }), @whois_record.json
|
'disclaimer' => 'disclaimer',
|
||||||
|
'registration_deadline' => registration_deadline.to_s }), @whois_record.json
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_updates_whois_record_from_auction_when_payment_received
|
def test_updates_whois_record_from_auction_when_payment_received
|
||||||
@auction.update!(domain: 'domain.test', status: Auction.statuses[:payment_received])
|
@auction.update!(domain: 'domain.test',
|
||||||
|
status: Auction.statuses[:payment_received],
|
||||||
|
registration_deadline: registration_deadline)
|
||||||
@whois_record.update!(name: 'domain.test')
|
@whois_record.update!(name: 'domain.test')
|
||||||
@whois_record.update_from_auction(@auction)
|
@whois_record.update_from_auction(@auction)
|
||||||
@whois_record.reload
|
@whois_record.reload
|
||||||
|
|
||||||
assert_equal ({ 'name' => 'domain.test',
|
assert_equal ({ 'name' => 'domain.test',
|
||||||
'status' => ['PendingRegistration'],
|
'status' => ['PendingRegistration'],
|
||||||
'disclaimer' => 'disclaimer' }), @whois_record.json
|
'disclaimer' => 'disclaimer',
|
||||||
|
'registration_deadline' => registration_deadline.to_s }), @whois_record.json
|
||||||
|
end
|
||||||
|
|
||||||
|
def registration_deadline
|
||||||
|
Time.zone.now + 10.days
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Add table
Add a link
Reference in a new issue