diff --git a/.gitignore b/.gitignore index 668b50574..ae8292f38 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ todo ## Environment normalisation: /.bundle /vendor/bundle +.idea +# editor tmp files +*.*~ # these should all be checked in to normalise the environment: # Gemfile.lock, .ruby-version, .ruby-gemset diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb index 7c90c1a49..3240db931 100644 --- a/app/controllers/admin/domains_controller.rb +++ b/app/controllers/admin/domains_controller.rb @@ -52,7 +52,7 @@ class Admin::DomainsController < AdminController redirect_to [:admin, @domain] else build_associations - flash.now[:alert] = I18n.t('failed_to_update_domain') + flash.now[:alert] = I18n.t('failed_to_update_domain') + ' ' + @domain.errors.full_messages.join(", ") render 'edit' end end diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 1e778b839..5b31a0dad 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -49,14 +49,17 @@ class Epp::DomainsController < EppController def update authorize! :update, @domain, @password - - if @domain.update(params[:parsed_frame], current_user) - if @domain.epp_pending_update.present? - render_epp_response '/epp/domains/success_pending' + begin + if @domain.update(params[:parsed_frame], current_user) + if @domain.epp_pending_update.present? + render_epp_response '/epp/domains/success_pending' + else + render_epp_response '/epp/domains/success' + end else - render_epp_response '/epp/domains/success' + handle_errors(@domain) end - else + rescue handle_errors(@domain) end end diff --git a/app/jobs/domain_delete_confirm_job.rb b/app/jobs/domain_delete_confirm_job.rb index 9b30a6cbd..7d890ef16 100644 --- a/app/jobs/domain_delete_confirm_job.rb +++ b/app/jobs/domain_delete_confirm_job.rb @@ -10,6 +10,7 @@ class DomainDeleteConfirmJob < Que::Job domain.clean_pendings! when RegistrantVerification::REJECTED DomainMailer.pending_delete_rejected_notification(domain).deliver_now + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) domain.poll_message!(:poll_pending_delete_rejected_by_registrant) domain.clean_pendings! end diff --git a/app/models/depp/user.rb b/app/models/depp/user.rb index 44c25bbb1..51ce3f682 100644 --- a/app/models/depp/user.rb +++ b/app/models/depp/user.rb @@ -18,7 +18,7 @@ module Depp def server client_cert = File.read(ENV['cert_path']) client_key = File.read(ENV['key_path']) - port = Rails.env.test? ? 701 : ENV['epp_port'] + port = ENV['epp_port'] || '700' @server_cache ||= Epp::Server.new({ server: ENV['epp_hostname'], diff --git a/app/models/domain.rb b/app/models/domain.rb index 3ec4315d1..370d83a87 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -12,9 +12,14 @@ class Domain < ActiveRecord::Base # TODO: should we user validates_associated :registrant here? has_many :admin_domain_contacts - accepts_nested_attributes_for :admin_domain_contacts, allow_destroy: true + accepts_nested_attributes_for :admin_domain_contacts, allow_destroy: !:admin_change_prohibited?, reject_if: :admin_change_prohibited? has_many :tech_domain_contacts - accepts_nested_attributes_for :tech_domain_contacts, allow_destroy: true + accepts_nested_attributes_for :tech_domain_contacts, allow_destroy: !:tech_change_prohibited?, reject_if: :tech_change_prohibited? + + def registrant_change_prohibited? + 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 @@ -108,7 +113,15 @@ class Domain < ActiveRecord::Base errors.add(:base, :invalid_auth_information_reserved) end - validate :check_permissions + validate :status_is_consistant + def status_is_consistant + has_error = (statuses.include?(DomainStatus::SERVER_HOLD) && statuses.include?(DomainStatus::SERVER_MANUAL_INZONE)) + errors.add(:domains, I18n.t(:object_status_prohibits_operation)) if has_error + end + + attr_accessor :is_admin + + validate :check_permissions, :unless => :is_admin def check_permissions return unless force_delete? errors.add(:base, I18n.t(:object_status_prohibits_operation)) @@ -174,6 +187,14 @@ class Domain < ActiveRecord::Base nameservers.select { |x| !x.hostname.end_with?(name) } end + def admin_change_prohibited? + statuses.include? DomainStatus::SERVER_ADMIN_CHANGE_PROHIBITED + end + + def tech_change_prohibited? + statuses.include? DomainStatus::SERVER_TECH_CHANGE_PROHIBITED + end + class << self def convert_period_to_time(period, unit) return (period.to_i / 365).years if unit == 'd' @@ -202,7 +223,7 @@ class Domain < ActiveRecord::Base count = 0 expired_pending_domains = Domain.where('registrant_verification_asked_at <= ?', expire_at) expired_pending_domains.each do |domain| - unless domain.pending_update? || domain.pending_delete? + unless domain.pending_update? || domain.pending_delete? || pending_delete_confirmation? msg = "#{Time.zone.now.utc} - ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, " \ "but no pendingDelete/pendingUpdate state present!\n" STDOUT << msg unless Rails.env.test? @@ -212,7 +233,7 @@ class Domain < ActiveRecord::Base if domain.pending_update? DomainMailer.pending_update_expired_notification_for_new_registrant(domain).deliver_now end - if domain.pending_delete? + if domain.pending_delete? || pending_delete_confirmation? DomainMailer.pending_delete_expired_notification(domain).deliver_now end domain.clean_pendings! @@ -358,8 +379,7 @@ class Domain < ActiveRecord::Base return false if statuses.include_any?(DomainStatus::DELETE_CANDIDATE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::CLIENT_RENEW_PROHIBITED, DomainStatus::PENDING_RENEW, DomainStatus::PENDING_TRANSFER, DomainStatus::PENDING_DELETE, - DomainStatus::PENDING_UPDATE, 'pendingDeleteConfirmation') - + DomainStatus::PENDING_UPDATE, DomainStatus::PENDING_DELETE_CONFIRMATION) true end @@ -379,6 +399,7 @@ class Domain < ActiveRecord::Base def clean_pendings! preclean_pendings self.pending_json = {} + statuses.delete[DomainStatus::PENDING_DELETE_CONFIRMATION] statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_DELETE) status_notes[DomainStatus::PENDING_UPDATE] = '' @@ -461,8 +482,9 @@ class Domain < ActiveRecord::Base return true if pending_delete? self.epp_pending_delete = true # for epp + # TODO: if this were to ever return true, that would be wrong. EPP would report sucess pending return true unless registrant_verification_asked? - set_pending_delete + pending_delete_confirmation! save(validate: false) # should check if this did succeed DomainMailer.pending_deleted(self).deliver_now @@ -639,7 +661,7 @@ class Domain < ActiveRecord::Base statuses.include?(DomainStatus::PENDING_UPDATE) && !statuses.include?(DomainStatus::FORCE_DELETE) end - # public api + # depricated not used, not valid def update_prohibited? pending_update_prohibited? && pending_delete_prohibited? end @@ -651,6 +673,7 @@ class Domain < ActiveRecord::Base def pending_update_prohibited? (statuses_was & [ + DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::CLIENT_UPDATE_PROHIBITED, DomainStatus::SERVER_UPDATE_PROHIBITED, DomainStatus::PENDING_CREATE, @@ -673,6 +696,14 @@ class Domain < ActiveRecord::Base statuses.include?(DomainStatus::PENDING_DELETE) && !statuses.include?(DomainStatus::FORCE_DELETE) end + def pending_delete_confirmation? + statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION + end + + def pending_delete_confirmation! + statuses << DomainStatus::PENDING_DELETE_CONFIRMATION unless pending_delete_prohibited? + end + def pending_delete_prohibited? (statuses_was & [ DomainStatus::CLIENT_DELETE_PROHIBITED, diff --git a/app/models/domain_status.rb b/app/models/domain_status.rb index 9f74b6c82..543ba0ff4 100644 --- a/app/models/domain_status.rb +++ b/app/models/domain_status.rb @@ -68,6 +68,7 @@ class DomainStatus < ActiveRecord::Base SERVER_REGISTRANT_CHANGE_PROHIBITED = 'serverRegistrantChangeProhibited' SERVER_ADMIN_CHANGE_PROHIBITED = 'serverAdminChangeProhibited' SERVER_TECH_CHANGE_PROHIBITED = 'serverTechChangeProhibited' + PENDING_DELETE_CONFIRMATION = 'pendingDeleteConfirmation' FORCE_DELETE = 'serverForceDelete' DELETE_CANDIDATE = 'deleteCandidate' EXPIRED = 'expired' @@ -77,7 +78,7 @@ class DomainStatus < ActiveRecord::Base CLIENT_DELETE_PROHIBITED, SERVER_DELETE_PROHIBITED, CLIENT_HOLD, SERVER_HOLD, CLIENT_RENEW_PROHIBITED, SERVER_RENEW_PROHIBITED, CLIENT_TRANSFER_PROHIBITED, SERVER_TRANSFER_PROHIBITED, CLIENT_UPDATE_PROHIBITED, SERVER_UPDATE_PROHIBITED, - INACTIVE, OK, PENDING_CREATE, PENDING_DELETE, PENDING_RENEW, PENDING_TRANSFER, + INACTIVE, OK, PENDING_CREATE, PENDING_DELETE, PENDING_DELETE_CONFIRMATION, PENDING_RENEW, PENDING_TRANSFER, PENDING_UPDATE, SERVER_MANUAL_INZONE, SERVER_REGISTRANT_CHANGE_PROHIBITED, SERVER_ADMIN_CHANGE_PROHIBITED, SERVER_TECH_CHANGE_PROHIBITED, FORCE_DELETE, DELETE_CANDIDATE, EXPIRED @@ -121,18 +122,6 @@ class DomainStatus < ActiveRecord::Base class << self def admin_statuses - # [ - # SERVER_HOLD, - # # sync with admin_statuses_map - # # SERVER_MANUAL_INZONE, - # # SERVER_RENEW_PROHIBITED, - # # SERVER_TRANSFER_PROHIBITED, - # # SERVER_REGISTRANT_CHANGE_PROHIBITED, - # # SERVER_ADMIN_CHANGE_PROHIBITED, - # # SERVER_TECH_CHANGE_PROHIBITED, - # SERVER_DELETE_PROHIBITED, - # SERVER_UPDATE_PROHIBITED - # ] admin_statuses_map.map(&:second) end @@ -162,7 +151,8 @@ class DomainStatus < ActiveRecord::Base PENDING_DELETE, PENDING_RENEW, PENDING_TRANSFER, - PENDING_UPDATE + PENDING_UPDATE, + PENDING_DELETE_CONFIRMATION ] end end diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index f28b76767..4a446d25c 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -149,6 +149,9 @@ class Epp::Domain < Domain code = frame.css('registrant').first.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)) + end regt = Registrant.find_by(code: code) if regt at[:registrant_id] = regt.id @@ -233,6 +236,11 @@ class Epp::Domain < Domain def admin_domain_contacts_attrs(frame, action) admin_attrs = domain_contact_attrs_from(frame, action, 'admin') + if action && !admin_attrs.empty? && admin_change_prohibited? + add_epp_error('2304', 'admin', DomainStatus::SERVER_ADMIN_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) + return [] + end + case action when 'rem' return destroy_attrs(admin_attrs, admin_domain_contacts) @@ -244,6 +252,11 @@ class Epp::Domain < Domain def tech_domain_contacts_attrs(frame, action) tech_attrs = domain_contact_attrs_from(frame, action, 'tech') + if action && !tech_attrs.empty? && tech_change_prohibited? + add_epp_error('2304', 'tech', DomainStatus::SERVER_TECH_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) + return [] + end + case action when 'rem' return destroy_attrs(tech_attrs, tech_domain_contacts) @@ -377,7 +390,6 @@ class Epp::Domain < Domain def domain_statuses_attrs(frame, action) status_list = domain_status_list_from(frame) - if action == 'rem' to_destroy = [] status_list.each do |x| @@ -424,20 +436,31 @@ class Epp::Domain < Domain def update(frame, current_user, verify = true) return super if frame.blank? at = {}.with_indifferent_access - at.deep_merge!(attrs_from(frame.css('chg'), current_user)) + at.deep_merge!(attrs_from(frame.css('chg'), current_user, 'chg')) at.deep_merge!(attrs_from(frame.css('rem'), current_user, 'rem')) at_add = attrs_from(frame.css('add'), current_user) at[:nameservers_attributes] += at_add[:nameservers_attributes] - at[:admin_domain_contacts_attributes] += at_add[:admin_domain_contacts_attributes] - at[:tech_domain_contacts_attributes] += at_add[:tech_domain_contacts_attributes] + + if !at[:admin_domain_contacts_attributes].empty? && admin_change_prohibited? + add_epp_error('2304', 'admin', DomainStatus::SERVER_ADMIN_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) + else + at[:admin_domain_contacts_attributes] += at_add[:admin_domain_contacts_attributes] + end + + if !at[:tech_domain_contacts_attributes].empty? && tech_change_prohibited? + add_epp_error('2304', 'tech', DomainStatus::SERVER_TECH_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) + else + at[:tech_domain_contacts_attributes] += at_add[:tech_domain_contacts_attributes] + end + at[:dnskeys_attributes] += at_add[:dnskeys_attributes] at[:statuses] = statuses - domain_statuses_attrs(frame.css('rem'), 'rem') + domain_statuses_attrs(frame.css('add'), 'add') # at[:statuses] += at_add[:domain_statuses_attributes] - if verify && + if errors.empty? && verify && Setting.request_confrimation_on_registrant_change_enabled && frame.css('registrant').present? && frame.css('registrant').attr('verified').to_s.downcase != 'yes' @@ -468,12 +491,12 @@ class Epp::Domain < Domain def apply_pending_delete! preclean_pendings - user = ApiUser.find(pending_json['current_user_id']) - frame = Nokogiri::XML(pending_json['frame']) + statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) statuses.delete(DomainStatus::PENDING_DELETE) DomainMailer.delete_confirmation(self).deliver_now - clean_pendings! if epp_destroy(frame, user, false) + # TODO: confirm that this actually makes sense + clean_pendings! if valid? && set_pending_delete! true end @@ -486,11 +509,10 @@ class Epp::Domain < Domain ) end - def epp_destroy(frame, user_id, verify = true) + def epp_destroy(frame, user_id) return false unless valid? - if verify && - Setting.request_confirmation_on_domain_deletion_enabled && + if Setting.request_confirmation_on_domain_deletion_enabled && frame.css('delete').attr('verified').to_s.downcase != 'yes' registrant_verification_asked!(frame.to_s, user_id) @@ -753,6 +775,8 @@ class Epp::Domain < Domain end ### ABILITIES ### + + # depricated -- this is redundant TODO: try to remove def can_be_deleted? begin errors.add(:base, :domain_status_prohibits_operation) @@ -764,6 +788,7 @@ class Epp::Domain < Domain def transferrable? (statuses & [ + DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_CREATE, DomainStatus::PENDING_UPDATE, DomainStatus::PENDING_DELETE, diff --git a/config/initializers/pdfkit.rb b/config/initializers/pdfkit.rb index b38887c6e..d7bbc64b8 100644 --- a/config/initializers/pdfkit.rb +++ b/config/initializers/pdfkit.rb @@ -1,5 +1,9 @@ PDFKit.configure do |config| - config.wkhtmltopdf = "#{Rails.root}/vendor/bin/wkhtmltopdf" + installed = %x(which wkhtmltopdf).chomp + if installed == "" then + installed = "#{Rails.root}/vendor/bin/wkhtmltopdf" + end + config.wkhtmltopdf = installed config.default_options = { page_size: 'A4', quiet: true diff --git a/lib/schemas/domain-eis-1.0.xsd b/lib/schemas/domain-eis-1.0.xsd index 222d3d6f4..eb2b420da 100644 --- a/lib/schemas/domain-eis-1.0.xsd +++ b/lib/schemas/domain-eis-1.0.xsd @@ -29,7 +29,7 @@ --> - + @@ -133,6 +133,22 @@ + + + + + + + + + + + + + + @@ -396,6 +412,10 @@ + + + + diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index c8c5f1ddf..9dd0f1a48 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -64,10 +64,15 @@ describe Domain do end context 'with valid attributes' do - before :all do + before :example do @domain = Fabricate(:domain) end + after do + @domain.delete + @domain = nil + end + it 'should be valid' do @domain.valid? @domain.errors.full_messages.should match_array([]) @@ -305,6 +310,108 @@ describe Domain do ]) end + it 'should should be manual in zone and held after force delete' do + @domain.valid? + @domain.outzone_at = Time.zone.now + 1.day # before redemption grace period + # what should this be? + # @domain.server_holdable?.should be true + @domain.statuses.include?(DomainStatus::SERVER_HOLD).should be false + @domain.statuses.include?(DomainStatus::SERVER_MANUAL_INZONE).should be false + @domain.set_force_delete + @domain.server_holdable?.should be false + @domain.statuses.include?(DomainStatus::SERVER_MANUAL_INZONE).should be true + @domain.statuses.include?(DomainStatus::SERVER_HOLD).should be false + end + + it 'should not allow update after force delete' do + @domain.valid? + @domain.pending_update_prohibited?.should be false + @domain.update_prohibited?.should be false + @domain.set_force_delete + @domain.pending_update_prohibited?.should be true + @domain.update_prohibited?.should be true + end + + context 'with time period settings' do + before :all do + @save_days_to_renew = Setting.days_to_renew_domain_before_expire + @save_warning_period = Setting.expire_warning_period + @save_grace_period = Setting.redemption_grace_period + end + + after :all do + Setting.days_to_renew_domain_before_expire = @save_days_to_renew + Setting.expire_warning_period = @save_warning_period + Setting.redemption_grace_period = @save_grace_period + end + + before :example do + @domain.valid? + end + + context 'with no renewal limit, renew anytime' do + before do + Setting.days_to_renew_domain_before_expire = 0 + end + + it 'should always renew with no policy' do + @domain.renewable?.should be true + end + + it 'should not allow to renew after force delete' do + @domain.set_force_delete + @domain.renewable?.should be false + end + end + + context 'with renew policy' do + before :all do + @policy = 30 + Setting.days_to_renew_domain_before_expire = @policy + end + + it 'should not allow renew before policy' do + @domain.valid_to = Time.zone.now.beginning_of_day + @policy.days * 2 + @domain.renewable?.should be false + end + + context 'ready to renew' do + before { @domain.valid_to = Time.zone.now + (@policy - 2).days } + + it 'should allow renew' do + @domain.renewable?.should be true + end + + it 'should not allow to renew after force delete' do + @domain.set_force_delete + @domain.renewable?.should be false + end + end + end + end + + it 'should start redemption grace period' do + Domain.start_redemption_grace_period + @domain.reload + @domain.statuses.include?(DomainStatus::SERVER_HOLD).should == false + + @domain.outzone_at = Time.zone.now + @domain.statuses << DomainStatus::SERVER_MANUAL_INZONE # this prohibits server_hold + @domain.save + + Domain.start_redemption_grace_period + @domain.reload + @domain.statuses.include?(DomainStatus::SERVER_HOLD).should == false + + @domain.statuses = [] + @domain.save + + Domain.start_redemption_grace_period + @domain.reload + @domain.statuses.include?(DomainStatus::SERVER_HOLD).should == true + end + + it 'should set expired status and update outzone_at and delete_at' do domain = Fabricate(:domain) domain.statuses.should == ['ok'] @@ -482,7 +589,7 @@ describe Domain do end context 'about registrant update confirm' do - before :all do + before :example do @domain.registrant_verification_token = 123 @domain.registrant_verification_asked_at = Time.zone.now @domain.statuses << DomainStatus::PENDING_UPDATE @@ -503,7 +610,7 @@ describe Domain do end context 'about registrant update confirm when domain is invalid' do - before :all do + before :example do @domain.registrant_verification_token = 123 @domain.registrant_verification_asked_at = Time.zone.now @domain.statuses << DomainStatus::PENDING_UPDATE diff --git a/spec/support/epp.rb b/spec/support/epp.rb index 24a86e3d9..6d57e7d9e 100644 --- a/spec/support/epp.rb +++ b/spec/support/epp.rb @@ -92,8 +92,10 @@ module Epp end def server + port = ENV['epp_port'] || 700 + hostname = ENV['epp_hostname'] || 'localhost' # tag and password not in use, add those at login xml - @server ||= Epp::Server.new({ server: 'localhost', port: 701, tag: '', password: '' }) + @server ||= Epp::Server.new({ server: hostname, port: port, tag: '', password: '' }) end def parse_response(raw)