Merge branch 'master' into 115693873-whodunnit_empty

This commit is contained in:
Stas Paaslane 2016-08-12 16:56:40 +03:00
commit e23b5fc9cf
125 changed files with 2167 additions and 714 deletions

View file

@ -95,6 +95,7 @@ class Ability
can :manage, ReservedDomain
can :manage, ZonefileSetting
can :manage, DomainVersion
can :manage, ContactVersion
can :manage, Pricelist
can :manage, User
can :manage, ApiUser

View file

@ -6,7 +6,6 @@ class AccountActivity < ActiveRecord::Base
belongs_to :bank_transaction
belongs_to :invoice
attr_accessor :registrar
CREATE = 'create'
RENEW = 'renew'
@ -24,14 +23,15 @@ class AccountActivity < ActiveRecord::Base
end
def to_csv
attributes = %w(registrar description activity_type created_at sum)
attributes = %w(description activity_type created_at sum)
CSV.generate(headers: true) do |csv|
csv << %w(registrar description activity_type receipt_date sum)
all.each do |x| # rubocop:disable Rails/FindEach
x.registrar = Registrar.find(x.account_id).try(:code)
csv << attributes.map { |attr| x.send(attr) }
attrs = [x.account.registrar.try(:code)]
attrs += attributes.map { |attr| x.send(attr) }
csv << attrs
end
end
end

View file

@ -62,6 +62,10 @@ class ApiUser < User
username
end
def name
username
end
def queued_messages
registrar.messages.queued
end

View file

@ -40,7 +40,7 @@ class BlockedDomain < ActiveRecord::Base
def generate_json
h = HashWithIndifferentAccess.new
h[:name] = self.name
h[:status] = 'Blocked'
h[:status] = ['Blocked']
h
end

View file

@ -3,6 +3,7 @@ module Versions
extend ActiveSupport::Concern
included do
attr_accessor :version_loader
has_paper_trail class_name: "#{model_name}Version"
# add creator and updator
@ -55,4 +56,23 @@ module Versions
domains.each(&:touch_with_version)
end
end
module ClassMethods
def all_versions_for(ids, time)
ver_klass = paper_trail_version_class
from_history = ver_klass.where(item_id: ids.to_a).
order(:item_id).
preceding(time + 1, true).
select("distinct on (item_id) #{ver_klass.table_name}.*").
map do |ver|
o = new(ver.object)
o.version_loader = ver
ver.object_changes.to_h.each { |k, v| o[k]=v[-1] }
o
end
not_in_history = where(id: (ids.to_a - from_history.map(&:id)))
from_history + not_in_history
end
end
end

View file

@ -12,6 +12,10 @@ class Contact < ActiveRecord::Base
# TODO: remove later
has_many :depricated_statuses, class_name: 'DepricatedContactStatus', dependent: :destroy
has_paper_trail class_name: "ContactVersion", meta: { children: :children_log }
attr_accessor :legal_document_id
accepts_nested_attributes_for :legal_documents
validates :name, :phone, :email, :ident, :ident_type,
@ -29,18 +33,20 @@ class Contact < ActiveRecord::Base
uniqueness: { message: :epp_id_taken },
format: { with: /\A[\w\-\:\.\_]*\z/i, message: :invalid },
length: { maximum: 100, message: :too_long_contact_code }
validate :val_ident_type
validate :val_ident_valid_format?
validate :uniq_statuses?
validate :validate_html
validate :val_country_code
after_initialize do
self.statuses = [] if statuses.nil?
self.status_notes = {} if status_notes.nil?
self.ident_updated_at = Time.zone.now if new_record? && ident_updated_at.blank?
end
before_validation :set_ident_country_code
before_validation :to_upcase_country_code
before_validation :prefix_code
before_validation :strip_email
before_create :generate_auth_info
before_update :manage_emails
@ -56,16 +62,6 @@ class Contact < ActiveRecord::Base
end
end
before_save :manage_statuses
def manage_statuses
if domain_transfer # very ugly but need better workflow
self.statuses = statuses | [OK, LINKED]
return
end
manage_linked
manage_ok
end
after_save :update_related_whois_records
@ -76,7 +72,7 @@ class Contact < ActiveRecord::Base
ORG = 'org'
PRIV = 'priv'
BIRTHDAY = 'birthday'
BIRTHDAY = 'birthday'.freeze
PASSPORT = 'passport'
IDENT_TYPES = [
@ -168,7 +164,7 @@ class Contact < ActiveRecord::Base
end
def find_orphans
Contact.where('
where('
NOT EXISTS(
select 1 from domains d where d.registrant_id = contacts.id
) AND NOT EXISTS(
@ -177,20 +173,49 @@ class Contact < ActiveRecord::Base
')
end
def find_linked
where('
EXISTS(
select 1 from domains d where d.registrant_id = contacts.id
) OR EXISTS(
select 1 from domain_contacts dc where dc.contact_id = contacts.id
)
')
end
def filter_by_states in_states
states = Array(in_states).dup
scope = all
# all contacts has state ok, so no need to filter by it
scope = scope.where("NOT contacts.statuses && ?::varchar[]", "{#{(STATUSES - [OK, LINKED]).join(',')}}") if states.delete(OK)
scope = scope.find_linked if states.delete(LINKED)
scope = scope.where("contacts.statuses @> ?::varchar[]", "{#{states.join(',')}}") if states.any?
scope
end
# To leave only new ones we need to check
# if contact was at any time used in domain.
# This can be checked by domain history.
# This can be checked by saved relations in children attribute
def destroy_orphans
STDOUT << "#{Time.zone.now.utc} - Destroying orphaned contacts\n" unless Rails.env.test?
orphans = find_orphans
unless Rails.env.test?
orphans.each do |m|
STDOUT << "#{Time.zone.now.utc} Contact.destroy_orphans: ##{m.id} (#{m.name})\n"
counter = Counter.new
find_orphans.find_each do |contact|
ver_scope = []
%w(admin_contacts tech_contacts registrant).each do |type|
ver_scope << "(children->'#{type}')::jsonb <@ json_build_array(#{contact.id})::jsonb"
end
next if DomainVersion.where("created_at > ?", Time.now - Setting.orphans_contacts_in_months.to_i.months).where(ver_scope.join(" OR ")).any?
next if contact.domains_present?
contact.destroy
counter.next
STDOUT << "#{Time.zone.now.utc} Contact.destroy_orphans: ##{contact.id} (#{contact.name})\n"
end
count = orphans.destroy_all.count
STDOUT << "#{Time.zone.now.utc} - Successfully destroyed #{count} orphaned contacts\n" unless Rails.env.test?
STDOUT << "#{Time.zone.now.utc} - Successfully destroyed #{counter} orphaned contacts\n" unless Rails.env.test?
end
def privs
@ -231,10 +256,31 @@ class Contact < ActiveRecord::Base
"EIS-#{id}"
end
# kind of decorator in order to always return statuses
# if we use separate decorator, then we should add it
# to too many places
def statuses
calculated = Array(read_attribute(:statuses))
calculated.delete(Contact::OK)
calculated.delete(Contact::LINKED)
calculated << Contact::OK if calculated.empty?# && valid?
calculated << Contact::LINKED if domains_present?
calculated.uniq
end
def statuses= arr
write_attribute(:statuses, Array(arr).uniq)
end
def to_s
name || '[no name]'
end
def val_ident_type
errors.add(:ident_type, :epp_ident_type_invalid, code: code) if !%w(org priv birthday).include?(ident_type)
end
def val_ident_valid_format?
case ident_country_code
when 'EE'.freeze
@ -247,6 +293,8 @@ class Contact < ActiveRecord::Base
if ident.size != 8 || !(ident =~/\A[0-9]{8}\z/)
errors.add(:ident, err_msg)
end
when BIRTHDAY
errors.add(:ident, err_msg) if id.blank? # only for create action right now. Later for all of them
end
end
end
@ -264,11 +312,6 @@ class Contact < ActiveRecord::Base
end
end
def uniq_statuses?
return true unless statuses.detect { |s| statuses.count(s) > 1 }
errors.add(:statuses, :not_uniq)
false
end
def org?
ident_type == ORG
@ -279,6 +322,10 @@ class Contact < ActiveRecord::Base
!org?
end
def birthday?
ident_type == BIRTHDAY
end
def generate_auth_info
return if @generate_auth_info_disabled
return if auth_info.present?
@ -330,22 +377,36 @@ class Contact < ActiveRecord::Base
# TODO: refactor, it should not allow to destroy with normal destroy,
# no need separate method
# should use only in transaction
def destroy_and_clean
def destroy_and_clean frame
if domains_present?
errors.add(:domains, :exist)
return false
end
legal_document_data = Epp::Domain.parse_legal_document_from_frame(frame)
if legal_document_data
doc = LegalDocument.create(
documentable_type: Contact,
document_type: legal_document_data[:type],
body: legal_document_data[:body]
)
self.legal_documents = [doc]
self.legal_document_id = doc.id
self.save
end
destroy
end
def set_ident_country_code
return true unless ident_country_code_changed? && ident_country_code.present?
code = Country.new(ident_country_code)
if code
self.ident_country_code = code.alpha2
else
errors.add(:ident, :invalid_country_code)
end
def to_upcase_country_code
self.ident_country_code = ident_country_code.upcase if ident_country_code
self.country_code = country_code.upcase if country_code
end
def val_country_code
errors.add(:ident, :invalid_country_code) unless Country.new(ident_country_code)
errors.add(:ident, :invalid_country_code) unless Country.new(country_code)
end
def related_domain_descriptions
@ -380,18 +441,15 @@ class Contact < ActiveRecord::Base
domain_contacts.present? || registrant_domains.present?
end
def manage_linked
if domains_present?
set_linked
else
unset_linked
end
end
def search_name
"#{code} #{name}"
end
def strip_email
self.email = email.to_s.strip
end
# what we can do load firstly by registrant
# if total is smaller than needed, the load more
@ -461,43 +519,6 @@ class Contact < ActiveRecord::Base
end
end
def set_linked
statuses << LINKED if statuses.detect { |s| s == LINKED }.blank?
end
def unset_linked
statuses.delete_if { |s| s == LINKED }
end
# rubocop:disable Metrics/CyclomaticComplexity
def manage_ok
return unset_ok unless valid?
case statuses.size
when 0
set_ok
when 1
set_ok if statuses == [LINKED]
when 2
return if statuses.sort == [LINKED, OK]
unset_ok
else
unset_ok
end
end
# rubocop:enable Metrics/CyclomaticComplexity
def unset_ok
statuses.delete_if { |s| s == OK }
end
def set_ok
statuses << OK if statuses.detect { |s| s == OK }.blank?
end
def linked?
statuses.include?(LINKED)
end
def update_prohibited?
(statuses & [
@ -525,9 +546,18 @@ class Contact < ActiveRecord::Base
]).present?
end
def update_related_whois_records
names = related_domain_descriptions.keys
UpdateWhoisRecordJob.enqueue(names, :domain) if names.present?
end
def update_related_whois_records
# not doing anything if no real changes
return if changes.slice(*(self.class.column_names - ["updated_at", "created_at", "statuses", "status_notes"])).empty?
names = related_domain_descriptions.keys
UpdateWhoisRecordJob.enqueue(names, :domain) if names.present?
end
def children_log
log = HashWithIndifferentAccess.new
log[:legal_documents]= [legal_document_id]
log
end
end

View file

@ -66,8 +66,9 @@ module Depp
def delete(domain_params)
xml = epp_xml.delete({
name: { value: domain_params[:name] }
}, Depp::Domain.construct_custom_params_hash(domain_params))
name: { value: domain_params[:name] }},
Depp::Domain.construct_custom_params_hash(domain_params),
(domain_params[:verified].present? && 'yes'))
current_user.request(xml)
end
@ -214,7 +215,8 @@ module Depp
rem_arr << { _anonymus: rem_anon } if rem_anon.any?
if domain_params[:registrant] != old_domain_params[:registrant]
chg = [{ registrant: { value: domain_params[:registrant] } }]
chg = [{ registrant: { value: domain_params[:registrant] } }] if !domain_params[:verified].present?
chg = [{ registrant: { value: domain_params[:registrant], attrs: { verified: 'yes' } } }] if domain_params[:verified]
end
add_arr = nil if add_arr.none?

View file

@ -60,14 +60,15 @@ class Directo < ActiveRecord::Base
end
def self.send_monthly_invoices
def self.send_monthly_invoices(debug: false)
@debug = debug
I18n.locale = :et
month = Time.now - 1.month
invoices_until = month.end_of_month
date_format = "%Y-%m-%d"
invoice_counter= Counter.new
min_directo = Setting.invoice_number_min.presence.try(:to_i)
min_directo = Setting.directo_monthly_number_min.presence.try(:to_i)
max_directo = Setting.directo_monthly_number_max.presence.try(:to_i)
last_directo = [Setting.directo_monthly_number_last.presence.try(:to_i), min_directo].compact.max || 0
if max_directo && max_directo <= last_directo
@ -100,8 +101,8 @@ class Directo < ActiveRecord::Base
"ProductName" => ".#{pricelist.category} registreerimine: #{pricelist.years_amount} aasta",
"UnitPriceWoVAT" => pricelist.price_decimal/pricelist.years_amount
}
hash["StartDate"] = (activity.created_at + year.year).strftime(date_format) if year > 1
hash["EndDate"] = (activity.created_at + year.year + 1).strftime(date_format) if year > 1
hash["StartDate"] = (activity.created_at + (year-1).year).end_of_month.strftime(date_format) if year > 1
hash["EndDate"] = (activity.created_at + (year-1).year + 1).end_of_month.strftime(date_format) if year > 1
if items.has_key?(hash)
items[hash]["Quantity"] += 1
@ -112,8 +113,10 @@ class Directo < ActiveRecord::Base
end
#adding prepaiments
registrar_activities.where(activity_type: [AccountActivity::ADD_CREDIT]).each do |activity|
hash = {"ProductID" => Setting.directo_receipt_product_name, "Unit" => "tk", "ProductName" => "Domeenide ettemaks", "UnitPriceWoVAT"=>activity.sum}
if items.any?
total = 0
items.each{ |key, val| total += val["Quantity"] * key["UnitPriceWoVAT"] }
hash = {"ProductID" => Setting.directo_receipt_product_name, "Unit" => "tk", "ProductName" => "Domeenide ettemaks", "UnitPriceWoVAT"=>total}
items[hash] = {"RN"=>counter.next, "RR" => counter.now, "Quantity"=> -1}
end
@ -141,10 +144,14 @@ class Directo < ActiveRecord::Base
data = builder.to_xml.gsub("\n",'')
response = RestClient::Request.execute(url: ENV['directo_invoice_url'], method: :post, payload: {put: "1", what: "invoice", xmldata: data}, verify_ssl: false).to_s
Setting.directo_monthly_number_last = directo_next
Nokogiri::XML(response).css("Result").each do |res|
Directo.create!(response: res.as_json.to_h, invoice_number: directo_next)
Rails.logger.info("[DIRECTO] Invoice #{res.attributes["docid"].value} was pushed and return is #{res.as_json.to_h.inspect}")
if @debug
STDOUT << "#{Time.zone.now.utc} - Directo xml had to be sent #{data}\n"
else
Setting.directo_monthly_number_last = directo_next
Nokogiri::XML(response).css("Result").each do |res|
Directo.create!(request: data, response: res.as_json.to_h, invoice_number: directo_next)
Rails.logger.info("[DIRECTO] Invoice #{res.attributes["docid"].value} was pushed and return is #{res.as_json.to_h.inspect}")
end
end
else
Rails.logger.info("[DIRECTO] Registrar #{registrar.id} has nothing to be sent to Directo")

View file

@ -7,6 +7,8 @@ class Domain < ActiveRecord::Base
attr_accessor :roles
attr_accessor :legal_document_id
# TODO: whois requests ip whitelist for full info for own domains and partial info for other domains
# TODO: most inputs should be trimmed before validatation, probably some global logic?
@ -345,7 +347,8 @@ class Domain < ActiveRecord::Base
end
# state change shouln't be
# state changes may be done low-level - no validation
# in this metod we still save PaperTrail log.
def clean_pendings_lowlevel
statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
statuses.delete(DomainStatus::PENDING_UPDATE)
@ -354,13 +357,23 @@ class Domain < ActiveRecord::Base
status_notes[DomainStatus::PENDING_UPDATE] = ''
status_notes[DomainStatus::PENDING_DELETE] = ''
update_columns(
hash = {
registrant_verification_token: nil,
registrant_verification_asked_at: nil,
pending_json: {},
status_notes: status_notes,
statuses: statuses.presence || [DomainStatus::OK]
)
statuses: statuses.presence || [DomainStatus::OK],
# need this column in order to update PaperTrail version properly
updated_at: Time.now.utc
}
# PaperTrail
self.attributes = hash
record_update
clear_version_instance!
reset_transaction_id
update_columns(hash)
end
def pending_update!
@ -384,6 +397,7 @@ class Domain < ActiveRecord::Base
self.registrant_verification_token = token
self.registrant_verification_asked_at = asked_at
set_pending_update
touch_always_version
pending_json['new_registrant_id'] = new_registrant_id
pending_json['new_registrant_email'] = new_registrant_email
pending_json['new_registrant_name'] = new_registrant_name
@ -464,7 +478,8 @@ class Domain < ActiveRecord::Base
def validate_nameserver_ips
nameservers.to_a.reject(&:marked_for_destruction?).each do |ns|
next unless ns.hostname.end_with?(".#{name}")
next if ns.ipv4.present?
next if ns.ipv4.present? || ns.ipv6.present?
errors.add(:nameservers, :invalid) if errors[:nameservers].blank?
ns.errors.add(:ipv4, :blank)
end
@ -561,7 +576,7 @@ class Domain < ActiveRecord::Base
statuses << DomainStatus::SERVER_MANUAL_INZONE
end
self.force_delete_at = Time.zone.now + Setting.redemption_grace_period.days unless force_delete_at
self.force_delete_at = (Time.zone.now + (Setting.redemption_grace_period.days + 1.day)).utc.beginning_of_day unless force_delete_at
transaction do
save!(validate: false)
registrar.messages.create!(
@ -590,7 +605,7 @@ class Domain < ActiveRecord::Base
def set_graceful_expired
self.outzone_at = valid_to + Setting.expire_warning_period.days
self.delete_at = outzone_at + Setting.redemption_grace_period.days
self.delete_at = (outzone_at + (Setting.redemption_grace_period.days + 1.day)).utc.beginning_of_day
self.statuses |= [DomainStatus::EXPIRED]
end
@ -598,7 +613,7 @@ class Domain < ActiveRecord::Base
# TODO: currently valid_to attribute update logic is open
# self.valid_to = valid_from + self.class.convert_period_to_time(period, period_unit)
self.outzone_at = Time.zone.now + Setting.expire_warning_period.days
self.delete_at = Time.zone.now + Setting.redemption_grace_period.days
self.delete_at = (Time.zone.now + (Setting.redemption_grace_period.days + 1.day)).utc.beginning_of_day
statuses << DomainStatus::EXPIRED
end
@ -641,16 +656,7 @@ class Domain < ActiveRecord::Base
end
def pending_update_prohibited?
(statuses_was & [
DomainStatus::PENDING_DELETE_CONFIRMATION,
DomainStatus::CLIENT_UPDATE_PROHIBITED,
DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::PENDING_CREATE,
DomainStatus::PENDING_UPDATE,
DomainStatus::PENDING_DELETE,
DomainStatus::PENDING_RENEW,
DomainStatus::PENDING_TRANSFER
]).present?
(statuses_was & DomainStatus::UPDATE_PROHIBIT_STATES).present?
end
def set_pending_update
@ -674,17 +680,7 @@ class Domain < ActiveRecord::Base
end
def pending_delete_prohibited?
(statuses_was & [
DomainStatus::CLIENT_DELETE_PROHIBITED,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::CLIENT_UPDATE_PROHIBITED,
DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::PENDING_CREATE,
DomainStatus::PENDING_RENEW,
DomainStatus::PENDING_TRANSFER,
DomainStatus::PENDING_UPDATE,
DomainStatus::PENDING_DELETE
]).present?
(statuses_was & DomainStatus::DELETE_PROHIBIT_STATES).present?
end
# let's use positive method names
@ -725,8 +721,10 @@ class Domain < ActiveRecord::Base
log[:admin_contacts] = admin_contact_ids
log[:tech_contacts] = tech_contact_ids
log[:nameservers] = nameserver_ids
log[:dnskeys] = dnskey_ids
log[:domain_statuses]= domain_status_ids
log[:legal_documents]= [legal_document_id]
log[:registrant] = [registrant_id]
log[:domain_statuses] = domain_status_ids
log
end

View file

@ -68,25 +68,27 @@ class DomainCron
marked
end
def self.start_delete_period
begin
STDOUT << "#{Time.zone.now.utc} - Setting delete_candidate to domains\n" unless Rails.env.test?
#doing nothing, deprecated
d = Domain.where('delete_at <= ?', Time.zone.now)
marked = 0
real = 0
d.each do |domain|
next unless domain.delete_candidateable?
real += 1
domain.statuses << DomainStatus::DELETE_CANDIDATE
STDOUT << "#{Time.zone.now.utc} DomainCron.start_delete_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test?
::PaperTrail.whodunnit = "cron - #{__method__}"
domain.save(validate: false) and marked += 1
end
ensure # the operator should see what was accomplished
STDOUT << "#{Time.zone.now.utc} - Finished setting delete_candidate - #{marked} out of #{real} successfully set\n" unless Rails.env.test?
end
marked
def self.start_delete_period
# begin
# STDOUT << "#{Time.zone.now.utc} - Setting delete_candidate to domains\n" unless Rails.env.test?
#
# d = Domain.where('delete_at <= ?', Time.zone.now)
# marked = 0
# real = 0
# d.each do |domain|
# next unless domain.delete_candidateable?
# real += 1
# domain.statuses << DomainStatus::DELETE_CANDIDATE
# STDOUT << "#{Time.zone.now.utc} DomainCron.start_delete_period: ##{domain.id} (#{domain.name})\n" unless Rails.env.test?
# ::PaperTrail.whodunnit = "cron - #{__method__}"
# domain.save(validate: false) and marked += 1
# end
# ensure # the operator should see what was accomplished
# STDOUT << "#{Time.zone.now.utc} - Finished setting delete_candidate - #{marked} out of #{real} successfully set\n" unless Rails.env.test?
# end
# marked
end
def self.destroy_delete_candidates
@ -99,10 +101,23 @@ class DomainCron
destroy_with_message x
STDOUT << "#{Time.zone.now.utc} Domain.destroy_delete_candidates: by deleteCandidate ##{x.id} (#{x.name})\n" unless Rails.env.test?
c += 1
Domain.where('delete_at <= ?', Time.zone.now).each do |x|
next unless x.delete_candidateable?
x.statuses << DomainStatus::DELETE_CANDIDATE
# If domain successfully saved, add it to delete schedule
if x.save(validate: false)
::PaperTrail.whodunnit = "cron - #{__method__}"
DomainDeleteJob.enqueue(x.id, run_at: rand(((24*60) - (DateTime.now.hour * 60 + DateTime.now.minute))).minutes.from_now)
STDOUT << "#{Time.zone.now.utc} Domain.destroy_delete_candidates: job added by deleteCandidate status ##{x.id} (#{x.name})\n" unless Rails.env.test?
c += 1
end
end
Domain.where('force_delete_at <= ?', Time.zone.now).each do |x|
DomainDeleteJob.enqueue(x.id, run_at: rand(((24*60) - (DateTime.now.hour * 60 + DateTime.now.minute))).minutes.from_now)
STDOUT << "#{Time.zone.now.utc} DomainCron.destroy_delete_candidates: job added by force delete time ##{x.id} (#{x.name})\n" unless Rails.env.test?
::PaperTrail.whodunnit = "cron - #{__method__} case force_deleted_at"
WhoisRecord.where(domain_id: x.id).destroy_all
destroy_with_message x
@ -110,7 +125,7 @@ class DomainCron
c += 1
end
STDOUT << "#{Time.zone.now.utc} - Successfully destroyed #{c} domains\n" unless Rails.env.test?
STDOUT << "#{Time.zone.now.utc} - Job destroy added for #{c} domains\n" unless Rails.env.test?
end
# rubocop: enable Metrics/AbcSize

View file

@ -20,28 +20,7 @@ class DomainMailModel
domain_info
compose
end
def registrant_updated_notification_for_new_registrant
registrant
subject(:registrant_updated_notification_for_new_registrant_subject)
domain_info
compose
end
def registrant_updated_notification_for_old_registrant
registrant_pending
registrant_old
subject(:registrant_updated_notification_for_old_registrant_subject)
new_registrant = Registrant.find @domain.pending_json['new_registrant_id']
@params[:registrant_name] = new_registrant.name
@params[:registrant_ident] = new_registrant.ident
@params[:registrant_priv] = new_registrant.priv?
@params[:registrant_email] = new_registrant.email
@params[:registrant_street] = new_registrant.street
@params[:registrant_city] = new_registrant.city
@params[:registrant_country] = new_registrant.country.name
compose
end
def pending_update_rejected_notification_for_new_registrant
registrant_pending

View file

@ -95,6 +95,29 @@ class DomainStatus < ActiveRecord::Base
SERVER_ADMIN_CHANGE_PROHIBITED, SERVER_TECH_CHANGE_PROHIBITED
]
UPDATE_PROHIBIT_STATES = [
DomainStatus::PENDING_DELETE_CONFIRMATION,
DomainStatus::CLIENT_UPDATE_PROHIBITED,
DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::PENDING_CREATE,
DomainStatus::PENDING_UPDATE,
DomainStatus::PENDING_DELETE,
DomainStatus::PENDING_RENEW,
DomainStatus::PENDING_TRANSFER
]
DELETE_PROHIBIT_STATES = [
DomainStatus::CLIENT_DELETE_PROHIBITED,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::CLIENT_UPDATE_PROHIBITED,
DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::PENDING_CREATE,
DomainStatus::PENDING_RENEW,
DomainStatus::PENDING_TRANSFER,
DomainStatus::PENDING_UPDATE,
DomainStatus::PENDING_DELETE
]
def epp_code_map
{
'2302' => [ # Object exists

View file

@ -37,10 +37,7 @@ class Epp::Contact < Contact
at[:country_code] = f.css('postalInfo addr cc').text if f.css('postalInfo addr cc').present?
at[:auth_info] = f.css('authInfo pw').text if f.css('authInfo pw').present?
legal_frame = f.css('legalDocument').first
if legal_frame.present?
at[:legal_documents_attributes] = legal_document_attrs(legal_frame)
end
at.merge!(ident_attrs(f.css('ident').first)) if new_record
at
end
@ -104,6 +101,7 @@ class Epp::Contact < Contact
res
end
end
delegate :ident_attr_valid?, to: :class
@ -134,6 +132,9 @@ class Epp::Contact < Contact
'2302' => [ # Object exists
[:code, :epp_id_taken]
],
'2304' => [ # Object status prohibits operation
[:ident_type, :epp_ident_type_invalid, { value: { obj: 'code', val: code}, interpolation: {code: code}}]
],
'2305' => [ # Association exists
[:domains, :exist]
],
@ -143,7 +144,7 @@ class Epp::Contact < Contact
end
# rubocop:disable Metrics/AbcSize
def update_attributes(frame)
def update_attributes(frame, current_user)
return super if frame.blank?
at = {}.with_indifferent_access
at.deep_merge!(self.class.attrs_from(frame.css('chg'), new_record: false))
@ -152,8 +153,14 @@ class Epp::Contact < Contact
at[:statuses] = statuses - statuses_attrs(frame.css('rem'), 'rem') + statuses_attrs(frame.css('add'), 'add')
end
legal_frame = frame.css('legalDocument').first
at[:legal_documents_attributes] = self.class.legal_document_attrs(legal_frame)
# legal_frame = frame.css('legalDocument').first
# at[:legal_documents_attributes] = self.class.legal_document_attrs(legal_frame)
if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame))
frame.css("legalDocument").first.content = doc.path if doc && doc.persisted?
self.legal_document_id = doc.id
end
self.deliver_emails = true # turn on email delivery for epp
@ -169,6 +176,8 @@ class Epp::Contact < Contact
elsif ident_type == "birthday" && !ident[/\A\d{4}-\d{2}-\d{2}\z/] && (Date.parse(ident) rescue false)
at.merge!(ident: ident_frame.text)
at.merge!(ident_country_code: ident_frame.attr('cc')) if ident_frame.attr('cc').present?
elsif ident_type == "birthday" && ident_country_code.blank?
at.merge!(ident_country_code: ident_frame.attr('cc'))
elsif ident_type.blank? && ident_country_code.blank?
at.merge!(ident_type: ident_frame.attr('type'))
at.merge!(ident_country_code: ident_frame.attr('cc')) if ident_frame.attr('cc').present?
@ -180,6 +189,9 @@ class Epp::Contact < Contact
end
end
self.upid = current_user.registrar.id if current_user.registrar
self.up_date = Time.zone.now
super(at)
end
# rubocop:enable Metrics/AbcSize
@ -217,4 +229,29 @@ class Epp::Contact < Contact
status_list
end
def attach_legal_document(legal_document_data)
return unless legal_document_data
legal_documents.create(
document_type: legal_document_data[:type],
body: legal_document_data[:body]
)
end
def add_legal_file_to_new frame
legal_document_data = Epp::Domain.parse_legal_document_from_frame(frame)
return unless legal_document_data
doc = LegalDocument.create(
documentable_type: Contact,
document_type: legal_document_data[:type],
body: legal_document_data[:body]
)
self.legal_documents = [doc]
frame.css("legalDocument").first.content = doc.path if doc && doc.persisted?
self.legal_document_id = doc.id
end
end

View file

@ -11,7 +11,8 @@ class Epp::Domain < Domain
return if is_admin # this bad hack for 109086524, refactor later
return true if is_transfer || is_renewal
return unless update_prohibited? || delete_prohibited?
add_epp_error('2304', nil, nil, I18n.t(:object_status_prohibits_operation))
stat = (statuses & (DomainStatus::UPDATE_PROHIBIT_STATES + DomainStatus::DELETE_PROHIBIT_STATES)).first
add_epp_error('2304', 'status', stat, I18n.t(:object_status_prohibits_operation))
false
end
@ -39,29 +40,12 @@ class Epp::Domain < Domain
before_save :link_contacts
def link_contacts
# Based on bullet report
if new_record?
# new record does not have correct instance contacts entries thanks to epp
unlinked_contacts = [registrant]
unlinked_contacts << admin_domain_contacts.map(&:contact)
unlinked_contacts << tech_domain_contacts.map(&:contact)
unlinked_contacts.flatten!
else
unlinked_contacts = contacts.select { |c| !c.linked? } # speed up a bit
end
unlinked_contacts.each do |uc|
uc.domains_present = true # no need to fetch domains again
uc.save(validate: false)
end
#TODO: cleanup cache if we think to cache dynamic statuses
end
after_destroy :unlink_contacts
def unlink_contacts
contacts.each do |c|
c.domains_present = false
c.save(validate: false)
end
#TODO: cleanup cache if we think to cache dynamic statuses
end
class << self
@ -133,7 +117,8 @@ class Epp::Domain < Domain
[:base, :ds_data_not_allowed],
[:base, :key_data_not_allowed],
[:period, :not_a_number],
[:period, :not_an_integer]
[:period, :not_an_integer],
[:registrant, :cannot_be_missing]
],
'2308' => [
[:base, :domain_name_blocked, { value: { obj: 'name', val: name_dirty } }]
@ -155,10 +140,11 @@ class Epp::Domain < Domain
def attrs_from(frame, current_user, action = nil)
at = {}.with_indifferent_access
code = frame.css('registrant').first.try(:text)
registrant_frame = frame.css('registrant').first
code = registrant_frame.try(:text)
if code.present?
if action == 'chg' && registrant_change_prohibited?
add_epp_error('2304', nil, DomainStatus::SERVER_REGISTRANT_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation))
add_epp_error('2304', "status", DomainStatus::SERVER_REGISTRANT_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation))
end
regt = Registrant.find_by(code: code)
if regt
@ -166,7 +152,10 @@ class Epp::Domain < Domain
else
add_epp_error('2303', 'registrant', code, [:registrant, :not_found])
end
end
else
add_epp_error('2306', nil, nil, [:registrant, :cannot_be_missing])
end if registrant_frame
at[:name] = frame.css('name').text if new_record?
at[:registrar_id] = current_user.registrar.try(:id)
@ -195,9 +184,27 @@ class Epp::Domain < Domain
end
at[:dnskeys_attributes] = dnskeys_attrs(dnskey_frame, action)
at[:legal_documents_attributes] = legal_document_from(frame)
at
end
# Adding legal doc to domain and
# if something goes wrong - raise Rollback error
def add_legal_file_to_new frame
legal_document_data = Epp::Domain.parse_legal_document_from_frame(frame)
return unless legal_document_data
doc = LegalDocument.create(
documentable_type: Domain,
document_type: legal_document_data[:type],
body: legal_document_data[:body]
)
self.legal_documents = [doc]
frame.css("legalDocument").first.content = doc.path if doc && doc.persisted?
self.legal_document_id = doc.id
end
# rubocop: enable Metrics/PerceivedComplexity
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/MethodLength
@ -457,15 +464,6 @@ class Epp::Domain < Domain
status_list
end
def legal_document_from(frame)
ld = frame.css('legalDocument').first
return [] unless ld
[{
body: ld.text,
document_type: ld['type']
}]
end
# rubocop: disable Metrics/AbcSize
# rubocop: disable Metrics/CyclomaticComplexity
@ -477,6 +475,7 @@ class Epp::Domain < Domain
if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame))
frame.css("legalDocument").first.content = doc.path if doc && doc.persisted?
self.legal_document_id = doc.id
end
at_add = attrs_from(frame.css('add'), current_user, 'add')
@ -489,6 +488,11 @@ class Epp::Domain < Domain
# at[:statuses] += at_add[:domain_statuses_attributes]
if errors.empty? && verify
self.upid = current_user.registrar.id if current_user.registrar
self.up_date = Time.zone.now
end
if registrant_id && registrant.code == frame.css('registrant')
throw :epp_error, {
@ -515,18 +519,22 @@ class Epp::Domain < Domain
preclean_pendings
user = ApiUser.find(pending_json['current_user_id'])
frame = Nokogiri::XML(pending_json['frame'])
old_registrant_id = registrant_id
self.deliver_emails = true # turn on email delivery
self.statuses.delete(DomainStatus::PENDING_UPDATE)
self.upid = user.registrar.id if user.registrar
self.up_date = Time.zone.now
send_mail :registrant_updated_notification_for_old_registrant
return unless update(frame, user, false)
clean_pendings!
send_mail :registrant_updated_notification_for_new_registrant
WhoisRecord.find_by(domain_id: id).save # need to reload model
save! # for notification if everything fails
WhoisRecord.find_by(domain_id: id).save # need to reload model
DomainMailer.registrant_updated_notification_for_old_registrant(id, old_registrant_id, registrant_id, true).deliver
DomainMailer.registrant_updated_notification_for_new_registrant(id, old_registrant_id, registrant_id, true).deliver
true
end
@ -575,7 +583,7 @@ class Epp::Domain < Domain
msg: I18n.t(:object_status_prohibits_operation)
} unless pending_deletable?
self.delete_at = Time.zone.now + Setting.redemption_grace_period.days
self.delete_at = (Time.zone.now + (Setting.redemption_grace_period.days + 1.day)).utc.beginning_of_day
set_pending_delete
set_server_hold if server_holdable?
save(validate: false)

View file

@ -1,4 +1,7 @@
class LegalDocument < ActiveRecord::Base
include EppErrors
MIN_BODY_SIZE = (1.37 * 3.kilobytes).ceil
if ENV['legal_document_types'].present?
TYPES = ENV['legal_document_types'].split(',').map(&:strip)
else
@ -10,11 +13,22 @@ class LegalDocument < ActiveRecord::Base
belongs_to :documentable, polymorphic: true
validates :body, length: { minimum: (1.37 * 8.kilobytes).ceil }, if: ->(file){ file.path.blank? && !Rails.env.staging?}
validate :val_body_length, if: ->(file){ file.path.blank? && !Rails.env.staging?}
before_create :add_creator
before_save :save_to_filesystem
def epp_code_map
{
'2306' => [
[:body, :length]
]
}
end
def val_body_length
errors.add(:body, :length) if body.nil? || body.size < MIN_BODY_SIZE
end
def save_to_filesystem

View file

@ -12,4 +12,8 @@ class Message < ActiveRecord::Base
self.queued = false
save
end
def name
"-"
end
end

View file

@ -8,7 +8,7 @@ class Nameserver < ActiveRecord::Base
# scope :owned_by_registrar, -> (registrar) { joins(:domain).where('domains.registrar_id = ?', registrar.id) }
# rubocop: disable Metrics/LineLength
validates :hostname, format: { with: /\A(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\z/ }
validates :hostname, format: { with: /\A(([a-zA-Z0-9]|[a-zA-ZäöüõšžÄÖÜÕŠŽ0-9][a-zA-ZäöüõšžÄÖÜÕŠŽ0-9\-]*[a-zA-ZäöüõšžÄÖÜÕŠŽ0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\z/ }
# validates :ipv4, format: { with: /\A(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/, allow_blank: true }
# validates :ipv6, format: { with: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, allow_blank: true }
validate :val_ipv4
@ -16,20 +16,23 @@ class Nameserver < ActiveRecord::Base
# rubocop: enable Metrics/LineLength
before_validation :normalize_attributes
before_validation :check_puny_symbols
before_validation :check_label_length
delegate :name, to: :domain, prefix: true
def epp_code_map
{
'2302' => [
[:hostname, :taken, { value: { obj: 'hostAttr', val: hostname } }]
[:hostname, :taken, { value: { obj: 'hostAttr', val: {'hostName': hostname} } }]
],
'2005' => [
[:hostname, :invalid, { value: { obj: 'hostAttr', val: hostname } }],
[:hostname, :puny_to_long, { value: { obj: 'hostAttr', val: hostname } }],
[:ipv4, :invalid, { value: { obj: 'hostAddr', val: ipv4 } }],
[:ipv6, :invalid, { value: { obj: 'hostAddr', val: ipv6 } }]
],
'2306' => [
'2003' => [
[:ipv4, :blank]
]
}
@ -41,10 +44,26 @@ class Nameserver < ActiveRecord::Base
self.ipv6 = Array(ipv6).reject(&:blank?).map(&:strip).map(&:upcase)
end
def check_label_length
hostname_puny.split('.').each do |label|
errors.add(:hostname, :puny_to_long) if label.length > 63
end
end
def check_puny_symbols
regexp = /(\A|\.)..--/
errors.add(:hostname, :invalid) if hostname =~ regexp
end
def to_s
hostname
end
def hostname=(hostname)
self[:hostname] = SimpleIDN.to_unicode(hostname)
self[:hostname_puny] = SimpleIDN.to_ascii(hostname)
end
def val_ipv4
regexp = /\A(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/
ipv4.to_a.each do |ip|

View file

@ -30,10 +30,20 @@ class RegistrantUser < User
return false if issuer_organization != ACCEPTED_ISSUER
idc_data.force_encoding('UTF-8')
identity_code = idc_data.scan(/serialNumber=(\d+)/).flatten.first
country = idc_data.scan(/^\/C=(.{2})/).flatten.first
first_name = idc_data.scan(%r{/GN=(.+)/serialNumber}).flatten.first
last_name = idc_data.scan(%r{/SN=(.+)/GN}).flatten.first
# handling here new and old mode
if idc_data.starts_with?("/")
identity_code = idc_data.scan(/serialNumber=(\d+)/).flatten.first
country = idc_data.scan(/^\/C=(.{2})/).flatten.first
first_name = idc_data.scan(%r{/GN=(.+)/serialNumber}).flatten.first
last_name = idc_data.scan(%r{/SN=(.+)/GN}).flatten.first
else
parse_str = "," + idc_data
identity_code = parse_str.scan(/,serialNumber=(\d+)/).flatten.first
country = parse_str.scan(/,C=(.{2})/).flatten.first
first_name = parse_str.scan(/,GN=([^,]+)/).flatten.first
last_name = parse_str.scan(/,SN=([^,]+)/).flatten.first
end
u = where(registrant_ident: "#{country}-#{identity_code}").first_or_create
u.username = "#{first_name} #{last_name}"

View file

@ -65,7 +65,7 @@ class ReservedDomain < ActiveRecord::Base
def generate_json
h = HashWithIndifferentAccess.new
h[:name] = self.name
h[:status] = 'Reserved'
h[:status] = ['Reserved']
h
end