Add disputing logic for EPP

This commit is contained in:
Karl Erik Õunapuu 2020-04-24 16:04:58 +03:00
parent 7a7b2f9881
commit 0f2a290d64
17 changed files with 626 additions and 325 deletions

View file

@ -28,9 +28,8 @@ module Admin
# POST /admin/disputes
def create
@dispute = Dispute.new(dispute_params)
if @dispute.save
redirect_to @dispute, notice: 'Dispute was successfully created.'
redirect_to admin_disputes_url, notice: 'Dispute was successfully created.'
else
render :new
end
@ -39,16 +38,16 @@ module Admin
# PATCH/PUT /admin/disputes/1
def update
if @dispute.update(dispute_params)
redirect_to @dispute, notice: 'Dispute was successfully updated.'
redirect_to admin_disputes_url, notice: 'Dispute was successfully updated.'
else
render :edit
end
end
# DELETE /admin/disputes/1
def destroy
def delete
@dispute.destroy
redirect_to disputes_url, notice: 'Dispute was successfully destroyed.'
redirect_to admin_disputes_url, notice: 'Dispute was successfully destroyed.'
end
private
@ -60,7 +59,7 @@ module Admin
# Only allow a trusted parameter "white list" through.
def dispute_params
params.require(:dispute).permit(:domain_name, :password, :expires_at, :comment, :created_at)
params.require(:dispute).permit(:domain_name, :password, :starts_at, :comment)
end
end
end

View file

@ -104,6 +104,10 @@ module Epp
authorize! :update, @domain, @password
if @domain.update(params[:parsed_frame], current_user)
if @domain.disputed?
dispute = Dispute.active.find_by(domain_name: @domain.name)
dispute.close
end
if @domain.epp_pending_update.present?
render_epp_response '/epp/domains/success_pending'
else

View file

@ -31,6 +31,12 @@ class Registrant::DomainUpdateConfirmsController < RegistrantController
end
elsif params[:confirmed]
if @registrant_verification.domain_registrant_change_confirm!("email link, #{initiator}")
if @domain.disputed?
Rails.logger.info 'Closing domain dispute via RegistrantConfirmation'
dispute = Dispute.active.find_by(domain_name: @domain.name)
dispute.close
end
flash[:notice] = t(:registrant_domain_verification_confirmed)
redirect_to registrant_domain_update_confirm_path(@domain.id, confirmed: true)
else

View file

@ -4,10 +4,11 @@ class UpdateWhoisRecordJob < Que::Job
::PaperTrail.whodunnit = "job - #{self.class.name} - #{type}"
klass = case type
when 'reserved'then ReservedDomain
when 'blocked' then BlockedDomain
when 'domain' then Domain
end
when 'reserved' then ReservedDomain
when 'blocked' then BlockedDomain
when 'domain' then Domain
when 'disputed' then Dispute
end
Array(names).each do |name|
record = klass.find_by(name: name)
@ -19,8 +20,6 @@ class UpdateWhoisRecordJob < Que::Job
end
end
def update_domain(domain)
domain.whois_record ? domain.whois_record.save : domain.create_whois_record
end
@ -33,6 +32,9 @@ class UpdateWhoisRecordJob < Que::Job
update_reserved(record)
end
def update_disputed(record)
update_reserved(record)
end
# 1. deleting own
# 2. trying to regenerate reserved in order domain is still in the list
@ -41,6 +43,7 @@ class UpdateWhoisRecordJob < Que::Job
BlockedDomain.find_by(name: name).try(:generate_data)
ReservedDomain.find_by(name: name).try(:generate_data)
Dispute.active.find_by(domain_name: name).try(:generate_data)
end
def delete_reserved(name)
@ -51,4 +54,8 @@ class UpdateWhoisRecordJob < Que::Job
def delete_blocked(name)
delete_reserved(name)
end
def delete_disputed(name)
delete_reserved(name)
end
end

View file

@ -1,5 +1,89 @@
# frozen_string_literal: true
class Dispute < ApplicationRecord
validates :domain_name, :password, :starts_at, :expires_at, :comment,
presence: true
validates :domain_name, :password, :starts_at, :expires_at, presence: true
validates_uniqueness_of :domain_name, case_sensitive: true
before_validation :fill_empty_passwords
before_validation :set_expiry_date
validate :validate_domain_name
with_options on: :admin do
validate :validate_start_date
end
before_save :set_expiry_date
before_save :sync_reserved_password
after_destroy :remove_data
scope :expired, -> { where('expires_at < ?', Time.zone.today) }
scope :active, -> { where('expires_at > ? AND closed = 0', Time.zone.today) }
scope :closed, -> { where(closed: true) }
alias_attribute :name, :domain_name
def set_expiry_date
return if starts_at.blank?
self.expires_at = starts_at + Setting.dispute_period_in_months.months
end
def generate_password
self.password = SecureRandom.hex
end
def generate_data
return if Domain.where(name: domain_name).any?
wr = Whois::Record.find_or_initialize_by(name: domain_name)
wr.json = @json = generate_json # we need @json to bind to class
wr.save
end
def close
self.closed = true
save!
end
def generate_json
h = HashWithIndifferentAccess.new
h[:name] = domain_name
h[:status] = ['Disputed']
h
end
def remove_data
UpdateWhoisRecordJob.enqueue domain_name, 'disputed'
end
def fill_empty_passwords
generate_password if password.blank?
end
def sync_reserved_password
reserved_domain = ReservedDomain.find_by(name: domain_name)
generate_password if password.blank?
unless reserved_domain.nil?
reserved_domain.password = password
reserved_domain.save!
end
generate_data
end
private
def validate_start_date
return if starts_at.nil?
errors.add(:starts_at, :past) if starts_at.past?
end
def validate_domain_name
return unless domain_name
zone = domain_name.split('.').last
supported_zone = DNS::Zone.origins.include?(zone)
errors.add(:domain_name, :unsupported_zone) unless supported_zone
end
end

View file

@ -68,6 +68,10 @@ module DNS
ReservedDomain.where(name: name).any?
end
def disputed?
Dispute.active.where(domain_name: name).any?
end
def auctionable?
!not_auctionable?
end
@ -81,7 +85,7 @@ module DNS
attr_reader :name
def not_auctionable?
blocked? || reserved?
blocked? || reserved? || disputed?
end
def zone_with_same_origin?

View file

@ -90,6 +90,7 @@ class Domain < ApplicationRecord
validates :transfer_code, presence: true
validate :validate_reservation
def validate_reservation
return if persisted? || !in_reserved_list?
@ -99,6 +100,7 @@ class Domain < ApplicationRecord
end
return if ReservedDomain.pw_for(name) == reserved_pw
errors.add(:base, :invalid_auth_information_reserved)
end
@ -170,7 +172,7 @@ class Domain < ApplicationRecord
end
attr_accessor :registrant_typeahead, :update_me,
:epp_pending_update, :epp_pending_delete, :reserved_pw
:epp_pending_update, :epp_pending_delete, :reserved_pw, :disputed_pw
self.ignored_columns = %w[legacy_id legacy_registrar_id legacy_registrant_id]
@ -275,6 +277,10 @@ class Domain < ApplicationRecord
@in_reserved_list ||= ReservedDomain.by_domain(name).any?
end
def disputed?
Dispute.active.where(domain_name: name).any?
end
def pending_transfer
transfers.find_by(status: DomainTransfer::PENDING)
end

View file

@ -58,7 +58,8 @@ class Epp::Domain < Domain
'2003' => [ # Required parameter missing
[:registrant, :blank],
[:registrar, :blank],
[:base, :required_parameter_missing_reserved]
[:base, :required_parameter_missing_reserved],
[:base, :required_parameter_missing_disputed]
],
'2004' => [ # Parameter value range error
[:dnskeys, :out_of_range,
@ -88,7 +89,8 @@ class Epp::Domain < Domain
[:transfer_code, :wrong_pw]
],
'2202' => [
[:base, :invalid_auth_information_reserved]
[:base, :invalid_auth_information_reserved],
[:base, :invalid_auth_information_disputed]
],
'2302' => [ # Object exists
[:name_dirty, :taken, { value: { obj: 'name', val: name_dirty } }],
@ -154,6 +156,7 @@ class Epp::Domain < Domain
at[:period_unit] = Epp::Domain.parse_period_unit_from_frame(frame) || 'y'
at[:reserved_pw] = frame.css('reserved > pw').text
at[:disputed_pw] = frame.css('disputed > pw').text
# at[:statuses] = domain_statuses_attrs(frame, action)
at[:nameservers_attributes] = nameservers_attrs(frame, action)
@ -475,6 +478,16 @@ class Epp::Domain < Domain
same_registrant_as_current = (registrant.code == frame.css('registrant').text)
if !same_registrant_as_current && disputed?
disputed_pw = frame.css('disputed > pw').text
if disputed_pw.blank?
add_epp_error('2304', nil, nil, 'Required parameter missing; disputed pw element required for dispute domains')
else
dispute = Dispute.active.find_by(domain_name: name, password: disputed_pw)
add_epp_error('2202', nil, nil, 'Invalid authorization information; invalid disputed>pw value') if dispute.nil?
end
end
if !same_registrant_as_current && errors.empty? && verify &&
Setting.request_confrimation_on_registrant_change_enabled &&
frame.css('registrant').present? &&
@ -706,6 +719,11 @@ class Epp::Domain < Domain
def can_be_deleted?
if disputed?
errors.add(:base, :domain_status_prohibits_operation)
return false
end
begin
errors.add(:base, :domain_status_prohibits_operation)
return false

View file

@ -46,6 +46,7 @@ class Setting < RailsSettings::Base
expire_warning_period
redemption_grace_period
expire_pending_confirmation
dispute_period_in_months
]
end

View file

@ -44,6 +44,8 @@ defaults: &defaults
registrar_ip_whitelist_enabled: false
api_ip_whitelist_enabled: false
dispute_period_in_months: 36
registry_juridical_name: "Eesti Interneti SA"
registry_reg_no: "90010019"
registry_email: "info@internet.ee"

View file

@ -8,3 +8,5 @@ en:
form:
password_hint: Generated automatically if left blank
optional: Not required by default
in_future: Must be at least today / in future

View file

@ -24,6 +24,8 @@ en:
key_data_not_allowed: 'keyData object is not allowed'
required_parameter_missing_reserved: 'Required parameter missing; reserved>pw element required for reserved domains'
invalid_auth_information_reserved: 'Invalid authorization information; invalid reserved>pw value'
required_parameter_missing_disputed: 'Required parameter missing; disputed pw element required for dispute domains'
invalid_auth_information_disputed: 'Invalid authorization information; invalid disputed>pw value'
domain_name_blocked: 'Data management policy violation: Domain name is blocked [name]'
name_dirty:
invalid: 'Domain name is invalid'
@ -631,6 +633,7 @@ en:
add_blocked_domain: 'Add domain to blocked list'
add_disputed_domain: 'Add domain to disputed list'
edit_pw: 'Edit Pw'
edit_dispute: 'Edit dispute'
optional: 'Optional'
test_registrar: "Test registrar"
verified_confirm: 'Verified status is for cases when current registrant is the one applying for the update. Legal document signed by the registrant is required. Are you sure this update is properly verified with the registrant?'

View file

@ -1,7 +1,6 @@
require_dependency 'epp_constraint'
Rails.application.routes.draw do
resources :disputes
# https://github.com/internetee/epp_proxy#translation-of-epp-calls
namespace :epp do
constraints(EppConstraint.new(:session)) do
@ -259,7 +258,11 @@ Rails.application.routes.draw do
get 'delete'
end
end
resources :disputes
resources :disputes do
member do
get 'delete'
end
end
resources :registrars do
resources :api_users, except: %i[index]

View file

@ -1,12 +1,12 @@
class CreateDisputes < ActiveRecord::Migration[5.2]
def change
create_table :disputes do |t|
t.string :domain_name
t.string :password
t.date :expires_at
t.date :starts_at
t.string :domain_name, null: false
t.string :password, null: false
t.date :expires_at, null: false
t.date :starts_at, null: false
t.text :comment
t.datetime :created_at
t.boolean :closed, null: false, default: false
t.timestamps
end

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,7 @@
<element name="ident" type="eis:identType" minOccurs="0" maxOccurs="1"/>
<element name="legalDocument" type="eis:legalDocType" minOccurs="0" maxOccurs="1"/>
<element name="reserved" type="eis:reservedType" minOccurs="0" maxOccurs="1"/>
<element name="disputed" type="eis:disputedType" minOccurs="0" maxOccurs="1"/>
</sequence>
</complexType>
@ -49,6 +50,16 @@
</restriction>
</simpleType>
<!--
Disputed for providing passwords for disputed domains
-->
<complexType name="disputedType">
<sequence>
<element name="pw" type="eis:pwType" minOccurs="0" maxOccurs="1"/>
</sequence>
</complexType>
<!--
Legal document, encoded in base64
-->

View file

@ -35,6 +35,11 @@ namespace :whois do
ReservedDomain.find_in_batches.each do |group|
UpdateWhoisRecordJob.enqueue group.map(&:name), 'reserved'
end
print "\n-----> Update disputed domains whois_records"
Dispute.active.find_in_batches.each do |group|
UpdateWhoisRecordJob.enqueue group.map(&:domain_name), 'disputed'
end
end
puts "\n-----> all done in #{(Time.zone.now.to_f - start).round(2)} seconds"
end