diff --git a/.codeclimate.yml b/.codeclimate.yml
index d079d891f..31da9b9cb 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -7,14 +7,19 @@ prepare:
plugins:
brakeman:
enabled: true
+ checks:
+ mass_assign_permit!:
+ enabled: false
bundler-audit:
enabled: true
duplication:
enabled: true
config:
languages:
- - ruby
- - javascript
+ ruby:
+ mass_threshold: 100
+ javascript:
+ mass_threshold: 100
eslint:
enabled: true
channel: eslint-5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3770abd4c..9afedbf17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,36 @@
+11.02.2021
+* Poll messages on locking and unlocking a domain [#1828](https://github.com/internetee/registry/issues/1828)
+* Registrar's prefix is now checked and added to contact id for info and check requests [#1832](https://github.com/internetee/registry/issues/1832)
+
+10.02.2021
+* Admin contact bulk change option for registrars [#1764](https://github.com/internetee/registry/issues/1764)
+* Option to remove email addresses from AWS SES Supression list [#1839](https://github.com/internetee/registry/issues/1839)
+* Added separate key for bounce API [#1842](https://github.com/internetee/registry/pull/1842)
+
+09.02.2021
+* Added new endpoint for WHOIS contact requests [#1794](https://github.com/internetee/registry/pull/1794)
+
+05.02.2021
+* Fixed IPv4 empty string issue in case of IPv6 only entries for IP whitelist [#1833](https://github.com/internetee/registry/issues/1833)
+
+02.02.2021
+* Fixed updateProhibited status not affecting bulk tech contact change operation [#1820](https://github.com/internetee/registry/pull/1820)
+
+01.02.2021
+* Improved tests for admin interface [#1805](https://github.com/internetee/registry/pull/1805)
+
+28.01.2021
+* Fixed transfer with shared admin and tech contacts [#1808](https://github.com/internetee/registry/issues/1808)
+* Improved error handling with double admin/tech contacts [#1758](https://github.com/internetee/registry/issues/1758)
+* Added CSV export option to admin [#1775](https://github.com/internetee/registry/issues/1775)
+* Improved DNSSEC key validation for illegal characters [#1790](https://github.com/internetee/registry/issues/1790)
+* Fix for whois record creation issue on releasing domain to auction [#1139](https://github.com/internetee/registry/issues/1139)
+* Fix for handling malformed request frames [#1825](https://github.com/internetee/registry/issues/1825)
+* Improved registrar account activity tests [#1824](https://github.com/internetee/registry/pull/1824)
+
+27.01.2021
+* Figaro update to 1.2.0 [#1823](https://github.com/internetee/registry/pull/1823)
+
26.01.2021
* Ruby update to 2.7 [#1791](https://github.com/internetee/registry/issues/1791)
diff --git a/Gemfile b/Gemfile
index 9bbcba254..82a0b4666 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,7 +9,7 @@ gem 'rest-client'
gem 'uglifier'
# load env
-gem 'figaro', '1.1.1'
+gem 'figaro', '~> 1.2'
# model related
gem 'activerecord-import'
@@ -92,3 +92,5 @@ group :test do
gem 'webdrivers'
gem 'webmock'
end
+
+gem 'aws-sdk-sesv2', '~> 1.16'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2a0bb55b1..08daf4226 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -148,6 +148,18 @@ GEM
attr_required (1.0.1)
autoprefixer-rails (10.0.0.2)
execjs
+ aws-eventstream (1.1.0)
+ aws-partitions (1.424.0)
+ aws-sdk-core (3.112.0)
+ aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (~> 1, >= 1.239.0)
+ aws-sigv4 (~> 1.1)
+ jmespath (~> 1.0)
+ aws-sdk-sesv2 (1.16.0)
+ aws-sdk-core (~> 3, >= 3.112.0)
+ aws-sigv4 (~> 1.1)
+ aws-sigv4 (1.2.2)
+ aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.16)
bindata (2.4.8)
bootsnap (1.4.8)
@@ -202,8 +214,8 @@ GEM
erubis (2.7.0)
execjs (2.7.0)
ffi (1.13.1)
- figaro (1.1.1)
- thor (~> 0.14)
+ figaro (1.2.0)
+ thor (>= 0.14.0, < 2)
globalid (0.4.2)
activesupport (>= 4.2.0)
gyoku (1.3.1)
@@ -226,6 +238,7 @@ GEM
i18n_data (0.10.0)
isikukood (0.1.2)
iso8601 (0.12.1)
+ jmespath (1.4.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@@ -484,6 +497,7 @@ DEPENDENCIES
active_interaction (~> 3.8)
activerecord-import
airbrake
+ aws-sdk-sesv2 (~> 1.16)
bootsnap (>= 1.1.0)
bootstrap-sass (~> 3.4)
cancancan
@@ -502,7 +516,7 @@ DEPENDENCIES
e_invoice!
epp!
epp-xml (= 1.1.0)!
- figaro (= 1.1.1)
+ figaro (~> 1.2)
haml (~> 5.0)
isikukood
iso8601 (= 0.12.1)
diff --git a/app/assets/javascripts/admin/application.coffee b/app/assets/javascripts/admin/application.coffee
index 4c7e6acb2..a149d89cf 100644
--- a/app/assets/javascripts/admin/application.coffee
+++ b/app/assets/javascripts/admin/application.coffee
@@ -8,6 +8,8 @@ $(window).load ->
$('[data-toggle="popover"]').popover()
+ $('[data-toggle="tooltip"]').tooltip()
+
# doublescroll
$('[data-doublescroll]').doubleScroll({
onlyIfScroll: false,
diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb
index 4a8e5961e..3e2d78671 100644
--- a/app/controllers/admin/domains_controller.rb
+++ b/app/controllers/admin/domains_controller.rb
@@ -27,8 +27,17 @@ module Admin
params[:q][:name_matches] = n_cache # we don't want to show wildcards in search form
end
end
-
@domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive?
+
+ respond_to do |format|
+ format.html do
+ @domains
+ end
+ format.csv do
+ raw_csv = @domains.to_csv
+ send_data raw_csv, filename: 'domains.csv', type: "#{Mime[:csv]}; charset=utf-8"
+ end
+ end
end
def show
diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb
index b62b3e063..6ee986a48 100644
--- a/app/controllers/api/v1/base_controller.rb
+++ b/app/controllers/api/v1/base_controller.rb
@@ -11,7 +11,7 @@ module Api
end
def authenticate_shared_key
- api_key = "Basic #{ENV['api_shared_key']}"
+ api_key = "Basic #{ENV['rwhois_internal_api_shared_key']}"
head(:unauthorized) unless api_key == request.authorization
end
diff --git a/app/controllers/api/v1/bounces_controller.rb b/app/controllers/api/v1/bounces_controller.rb
index fd10a3398..de2814250 100644
--- a/app/controllers/api/v1/bounces_controller.rb
+++ b/app/controllers/api/v1/bounces_controller.rb
@@ -1,7 +1,7 @@
module Api
module V1
class BouncesController < BaseController
- before_action :authenticate_shared_key
+ before_action :validate_shared_key_integrity
# POST api/v1/bounces/
def create
@@ -20,6 +20,13 @@ module Api
params.require(:data)
end
+
+ private
+
+ def validate_shared_key_integrity
+ api_key = "Basic #{ENV['rwhois_bounces_api_shared_key']}"
+ head(:unauthorized) unless api_key == request.authorization
+ end
end
end
end
diff --git a/app/controllers/api/v1/contact_requests_controller.rb b/app/controllers/api/v1/contact_requests_controller.rb
new file mode 100644
index 000000000..2b5977f59
--- /dev/null
+++ b/app/controllers/api/v1/contact_requests_controller.rb
@@ -0,0 +1,37 @@
+module Api
+ module V1
+ class ContactRequestsController < BaseController
+ before_action :authenticate_shared_key
+
+ # POST api/v1/contact_requests/
+ def create
+ return head(:bad_request) if contact_request_params[:email].blank?
+
+ contact_request = ContactRequest.save_record(contact_request_params)
+ render json: contact_request, status: :created
+ rescue StandardError
+ head(:bad_request)
+ end
+
+ def update
+ return head(:bad_request) if params[:id].blank?
+
+ process_id(params[:id])
+ end
+
+ def process_id(id)
+ record = ContactRequest.find_by(id: id)
+ return :not_found unless record
+
+ record.update_status(contact_request_params)
+ render json: record, status: :ok
+ rescue StandardError
+ head :bad_request
+ end
+
+ def contact_request_params
+ params.require(:contact_request).permit(:email, :whois_record_id, :name, :status, :ip)
+ end
+ end
+ end
+end
diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb
index 85305213b..65354ff48 100644
--- a/app/controllers/epp/contacts_controller.rb
+++ b/app/controllers/epp/contacts_controller.rb
@@ -14,7 +14,7 @@ module Epp
authorize! :check, Epp::Contact
ids = params[:parsed_frame].css('id').map(&:text)
- @results = Epp::Contact.check_availability(ids)
+ @results = Epp::Contact.check_availability(ids, reg: current_user.registrar.code)
render_epp_response '/epp/contacts/check'
end
@@ -93,7 +93,11 @@ module Epp
def find_contact
code = params[:parsed_frame].css('id').text.strip.upcase
- @contact = Epp::Contact.find_by!(code: code)
+ reg_code = current_user.registrar.code.upcase
+ arr = [code, "#{reg_code}:#{code}", "CID:#{code}", "CID:#{reg_code}:#{code}"]
+
+ contact = arr.find { |c| Epp::Contact.find_by(code: c).present? }
+ @contact = Epp::Contact.find_by!(code: contact || code)
end
#
diff --git a/app/controllers/registrar/admin_contacts_controller.rb b/app/controllers/registrar/admin_contacts_controller.rb
new file mode 100644
index 000000000..a1400b6dc
--- /dev/null
+++ b/app/controllers/registrar/admin_contacts_controller.rb
@@ -0,0 +1,18 @@
+class Registrar
+ class AdminContactsController < BulkChangeController
+ BASE_URL = URI.parse("#{ENV['repp_url']}domains/admin_contacts").freeze
+ ACTIVE_TAB = :admin_contact
+
+ def update
+ authorize! :manage, :repp
+ uri = BASE_URL
+ request = form_request(uri)
+ response = do_request(request, uri)
+ start_notice = t('.replaced')
+
+ process_response(response: response,
+ start_notice: start_notice,
+ active_tab: ACTIVE_TAB)
+ end
+ end
+end
diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb
index 801ab0516..74bbf89e8 100644
--- a/app/controllers/registrar/bulk_change_controller.rb
+++ b/app/controllers/registrar/bulk_change_controller.rb
@@ -26,6 +26,84 @@ class Registrar
private
+ def form_request(uri)
+ request = Net::HTTP::Patch.new(uri)
+ request.set_form_data(current_contact_id: params[:current_contact_id],
+ new_contact_id: params[:new_contact_id])
+ request.basic_auth(current_registrar_user.username,
+ current_registrar_user.plain_text_password)
+ request
+ end
+
+ def process_response(response:, start_notice: '', active_tab:)
+ parsed_response = JSON.parse(response.body, symbolize_names: true)
+
+ if response.code == '200'
+ notices = success_notices(parsed_response, start_notice)
+
+ flash[:notice] = notices.join(', ')
+ redirect_to registrar_domains_url
+ else
+ @error = response.code == '404' ? 'Contact(s) not found' : parsed_response[:message]
+ render file: 'registrar/bulk_change/new', locals: { active_tab: active_tab }
+ end
+ end
+
+ def success_notices(parsed_response, start_notice)
+ notices = [start_notice]
+
+ notices << "#{t('.affected_domains')}: " \
+ "#{parsed_response[:data][:affected_domains].join(', ')}"
+
+ if parsed_response[:data][:skipped_domains]
+ notices << "#{t('.skipped_domains')}: " \
+ "#{parsed_response[:data][:skipped_domains].join(', ')}"
+ end
+ notices
+ end
+
+ def do_request(request, uri)
+ response = if Rails.env.test?
+ do_test_request(request, uri)
+ elsif Rails.env.development?
+ do_dev_request(request, uri)
+ else
+ do_live_request(request, uri)
+ end
+ response
+ end
+
+ def do_live_request(request, uri)
+ client_cert = File.read(ENV['cert_path'])
+ client_key = File.read(ENV['key_path'])
+ Net::HTTP.start(uri.hostname, uri.port,
+ use_ssl: (uri.scheme == 'https'),
+ cert: OpenSSL::X509::Certificate.new(client_cert),
+ key: OpenSSL::PKey::RSA.new(client_key)) do |http|
+ http.request(request)
+ end
+ end
+
+ def do_dev_request(request, uri)
+ client_cert = File.read(ENV['cert_path'])
+ client_key = File.read(ENV['key_path'])
+ Net::HTTP.start(uri.hostname, uri.port,
+ use_ssl: (uri.scheme == 'https'),
+ verify_mode: OpenSSL::SSL::VERIFY_NONE,
+ cert: OpenSSL::X509::Certificate.new(client_cert),
+ key: OpenSSL::PKey::RSA.new(client_key)) do |http|
+ http.request(request)
+ end
+ end
+
+ def do_test_request(request, uri)
+ Net::HTTP.start(uri.hostname, uri.port,
+ use_ssl: (uri.scheme == 'https'),
+ verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
+ http.request(request)
+ end
+ end
+
def ready_to_renew?
domain_ids_for_bulk_renew.present? && params[:renew].present?
end
diff --git a/app/controllers/registrar/domain_transfers_controller.rb b/app/controllers/registrar/domain_transfers_controller.rb
index 584a50d33..e055c38d8 100644
--- a/app/controllers/registrar/domain_transfers_controller.rb
+++ b/app/controllers/registrar/domain_transfers_controller.rb
@@ -25,32 +25,7 @@ class Registrar
current_registrar_user.plain_text_password)
- if Rails.env.test?
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
- http.request(request)
- end
- elsif Rails.env.development?
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- else
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- end
+ response = do_request(request, uri)
parsed_response = JSON.parse(response.body, symbolize_names: true)
diff --git a/app/controllers/registrar/nameservers_controller.rb b/app/controllers/registrar/nameservers_controller.rb
index 52c43bb1d..3eb23cd48 100644
--- a/app/controllers/registrar/nameservers_controller.rb
+++ b/app/controllers/registrar/nameservers_controller.rb
@@ -18,32 +18,7 @@ class Registrar
request.basic_auth(current_registrar_user.username,
current_registrar_user.plain_text_password)
- if Rails.env.test?
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
- http.request(request)
- end
- elsif Rails.env.development?
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- else
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- end
+ response = do_request(request, uri)
parsed_response = JSON.parse(response.body, symbolize_names: true)
diff --git a/app/controllers/registrar/tech_contacts_controller.rb b/app/controllers/registrar/tech_contacts_controller.rb
index 001651250..cc9238730 100644
--- a/app/controllers/registrar/tech_contacts_controller.rb
+++ b/app/controllers/registrar/tech_contacts_controller.rb
@@ -1,62 +1,19 @@
class Registrar
class TechContactsController < BulkChangeController
+ BASE_URL = URI.parse("#{ENV['repp_url']}domains/contacts").freeze
+ ACTIVE_TAB = :technical_contact
+
def update
authorize! :manage, :repp
- uri = URI.parse("#{ENV['repp_url']}domains/contacts")
+ uri = BASE_URL
+ request = form_request(uri)
+ response = do_request(request, uri)
+ start_notice = t('.replaced')
- request = Net::HTTP::Patch.new(uri)
- request.set_form_data(current_contact_id: params[:current_contact_id],
- new_contact_id: params[:new_contact_id])
- request.basic_auth(current_registrar_user.username,
- current_registrar_user.plain_text_password)
-
- if Rails.env.test?
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
- http.request(request)
- end
- elsif Rails.env.development?
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- else
- client_cert = File.read(ENV['cert_path'])
- client_key = File.read(ENV['key_path'])
- response = Net::HTTP.start(uri.hostname, uri.port,
- use_ssl: (uri.scheme == 'https'),
- cert: OpenSSL::X509::Certificate.new(client_cert),
- key: OpenSSL::PKey::RSA.new(client_key)) do |http|
- http.request(request)
- end
- end
-
- parsed_response = JSON.parse(response.body, symbolize_names: true)
-
- if response.code == '200'
- notices = [t('.replaced')]
-
- notices << "#{t('.affected_domains')}: " \
- "#{parsed_response[:data][:affected_domains].join(', ')}"
-
- if parsed_response[:data][:skipped_domains]
- notices << "#{t('.skipped_domains')}: " \
- "#{parsed_response[:data][:skipped_domains].join(', ')}"
- end
-
- flash[:notice] = notices.join(', ')
- redirect_to registrar_domains_url
- else
- @error = response.code == '404' ? 'Contact(s) not found' : parsed_response[:message]
- render file: 'registrar/bulk_change/new', locals: { active_tab: :technical_contact }
- end
+ process_response(response: response,
+ start_notice: start_notice,
+ active_tab: ACTIVE_TAB)
end
end
end
diff --git a/app/controllers/repp/v1/domains/admin_contacts_controller.rb b/app/controllers/repp/v1/domains/admin_contacts_controller.rb
new file mode 100644
index 000000000..2e9a285eb
--- /dev/null
+++ b/app/controllers/repp/v1/domains/admin_contacts_controller.rb
@@ -0,0 +1,21 @@
+module Repp
+ module V1
+ module Domains
+ class AdminContactsController < BaseContactsController
+ def update
+ super
+
+ unless @new_contact.identical_to?(@current_contact)
+ @epp_errors << { code: 2304, msg: 'Admin contacts must be identical' }
+ end
+
+ return handle_errors if @epp_errors.any?
+
+ affected, skipped = AdminDomainContact.replace(@current_contact, @new_contact)
+ @response = { affected_domains: affected, skipped_domains: skipped }
+ render_success(data: @response)
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/repp/v1/domains/base_contacts_controller.rb b/app/controllers/repp/v1/domains/base_contacts_controller.rb
new file mode 100644
index 000000000..b601c5313
--- /dev/null
+++ b/app/controllers/repp/v1/domains/base_contacts_controller.rb
@@ -0,0 +1,31 @@
+module Repp
+ module V1
+ module Domains
+ class BaseContactsController < BaseController
+ before_action :set_current_contact, only: [:update]
+ before_action :set_new_contact, only: [:update]
+
+ def set_current_contact
+ @current_contact = current_user.registrar.contacts
+ .find_by!(code: contact_params[:current_contact_id])
+ end
+
+ def set_new_contact
+ @new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id])
+ end
+
+ def update
+ @epp_errors ||= []
+ @epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid?
+ end
+
+ private
+
+ def contact_params
+ params.require(%i[current_contact_id new_contact_id])
+ params.permit(:current_contact_id, :new_contact_id)
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb
index 75404e0c6..131615570 100644
--- a/app/controllers/repp/v1/domains/contacts_controller.rb
+++ b/app/controllers/repp/v1/domains/contacts_controller.rb
@@ -1,23 +1,9 @@
module Repp
module V1
module Domains
- class ContactsController < BaseController
- before_action :set_current_contact, only: [:update]
- before_action :set_new_contact, only: [:update]
-
- def set_current_contact
- @current_contact = current_user.registrar.contacts.find_by!(
- code: contact_params[:current_contact_id]
- )
- end
-
- def set_new_contact
- @new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id])
- end
-
+ class ContactsController < BaseContactsController
def update
- @epp_errors ||= []
- @epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid?
+ super
if @new_contact == @current_contact
@epp_errors << { code: 2304, msg: 'New contact must be different from current' }
@@ -29,13 +15,6 @@ module Repp
@response = { affected_domains: affected, skipped_domains: skipped }
render_success(data: @response)
end
-
- private
-
- def contact_params
- params.require(%i[current_contact_id new_contact_id])
- params.permit(:current_contact_id, :new_contact_id)
- end
end
end
end
diff --git a/app/lib/to_stdout.rb b/app/lib/to_stdout.rb
new file mode 100644
index 000000000..eeab82c15
--- /dev/null
+++ b/app/lib/to_stdout.rb
@@ -0,0 +1,6 @@
+class ToStdout
+ def self.msg(message)
+ time = Time.zone.now.utc
+ STDOUT << "#{time} - #{message}\n" unless Rails.env.test?
+ end
+end
diff --git a/app/models/admin_domain_contact.rb b/app/models/admin_domain_contact.rb
index 14907403d..7ccf3efcb 100644
--- a/app/models/admin_domain_contact.rb
+++ b/app/models/admin_domain_contact.rb
@@ -1,2 +1,26 @@
class AdminDomainContact < DomainContact
+ # rubocop:disable Metrics/AbcSize
+ # rubocop:disable Metrics/MethodLength
+ def self.replace(current_contact, new_contact)
+ affected_domains = []
+ skipped_domains = []
+ admin_contacts = where(contact: current_contact)
+
+ admin_contacts.each do |admin_contact|
+ if admin_contact.domain.bulk_update_prohibited?
+ skipped_domains << admin_contact.domain.name
+ next
+ end
+ begin
+ admin_contact.contact = new_contact
+ admin_contact.save!
+ affected_domains << admin_contact.domain.name
+ rescue ActiveRecord::RecordNotUnique
+ skipped_domains << admin_contact.domain.name
+ end
+ end
+ [affected_domains.sort, skipped_domains.sort]
+ end
+ # rubocop:enable Metrics/AbcSize
+ # rubocop:enable Metrics/MethodLength
end
diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb
index 73c6a0941..db4413829 100644
--- a/app/models/bounced_mail_address.rb
+++ b/app/models/bounced_mail_address.rb
@@ -1,5 +1,6 @@
class BouncedMailAddress < ApplicationRecord
validates :email, :message_id, :bounce_type, :bounce_subtype, :action, :status, presence: true
+ after_destroy :destroy_aws_suppression
def bounce_reason
"#{action} (#{status} #{diagnostic})"
@@ -25,4 +26,20 @@ class BouncedMailAddress < ApplicationRecord
diagnostic: bounced_record['diagnosticCode'],
}
end
+
+ def destroy_aws_suppression
+ return unless BouncedMailAddress.ses_configured?
+
+ res = Aws::SESV2::Client.new.delete_suppressed_destination(email_address: email)
+ res.successful?
+ rescue Aws::SESV2::Errors::ServiceError => e
+ logger.warn("Suppression not removed. #{e}")
+ end
+
+ def self.ses_configured?
+ ses ||= Aws::SESV2::Client.new
+ ses.config.credentials.access_key_id.present?
+ rescue Aws::Errors::MissingRegionError
+ false
+ end
end
diff --git a/app/models/concerns/contact/identical.rb b/app/models/concerns/contact/identical.rb
index f529e09ac..5327d1704 100644
--- a/app/models/concerns/contact/identical.rb
+++ b/app/models/concerns/contact/identical.rb
@@ -11,6 +11,13 @@ module Concerns::Contact::Identical
ident_country_code
org_name
]
+
+ IDENTICAL_ATTRIBUTES = %w[
+ ident
+ ident_type
+ ident_country_code
+ ].freeze
+
private_constant :IDENTIFIABLE_ATTRIBUTES
def identical(registrar)
@@ -20,6 +27,12 @@ module Concerns::Contact::Identical
.where.not(id: id).take
end
+ def identical_to?(contact)
+ IDENTICAL_ATTRIBUTES.all? do |attribute|
+ attributes[attribute] == contact.attributes[attribute]
+ end
+ end
+
private
def identifiable_hash
diff --git a/app/models/concerns/domain/bulk_updatable.rb b/app/models/concerns/domain/bulk_updatable.rb
new file mode 100644
index 000000000..a0aadb95f
--- /dev/null
+++ b/app/models/concerns/domain/bulk_updatable.rb
@@ -0,0 +1,17 @@
+module Concerns
+ module Domain
+ module BulkUpdatable
+ extend ActiveSupport::Concern
+
+ def bulk_update_prohibited?
+ discarded? || statuses_blocks_update?
+ end
+
+ def statuses_blocks_update?
+ prohibited_array = [DomainStatus::SERVER_UPDATE_PROHIBITED,
+ DomainStatus::CLIENT_UPDATE_PROHIBITED]
+ prohibited_array.any? { |block_status| statuses.include?(block_status) }
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/domain/registry_lockable.rb b/app/models/concerns/domain/registry_lockable.rb
index 4a759296d..2325e4b60 100644
--- a/app/models/concerns/domain/registry_lockable.rb
+++ b/app/models/concerns/domain/registry_lockable.rb
@@ -12,6 +12,7 @@ module Concerns
statuses << DomainStatus::SERVER_DELETE_PROHIBITED
statuses << DomainStatus::SERVER_TRANSFER_PROHIBITED
self.locked_by_registrant_at = Time.zone.now
+ alert_registrar_lock_changes!(lock: true)
save!
end
@@ -42,10 +43,21 @@ module Concerns
statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED)
statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED)
self.locked_by_registrant_at = nil
+ alert_registrar_lock_changes!(lock: false)
save!
end
end
+
+ def alert_registrar_lock_changes!(lock: true)
+ translation = lock ? 'locked' : 'unlocked'
+ registrar.notifications.create!(
+ text: I18n.t("notifications.texts.registrar_#{translation}",
+ domain_name: name),
+ attached_obj_id: name,
+ attached_obj_type: self.class.name
+ )
+ end
end
end
end
diff --git a/app/models/concerns/domain/releasable.rb b/app/models/concerns/domain/releasable.rb
index 4aa5faa58..0a17b062a 100644
--- a/app/models/concerns/domain/releasable.rb
+++ b/app/models/concerns/domain/releasable.rb
@@ -39,13 +39,15 @@ module Concerns
def release
if release_to_auction
- transaction do
- domain_name.sell_at_auction if domain_name.auctionable?
- destroy!
- registrar.notifications.create!(text: "#{I18n.t(:domain_deleted)}: #{name}",
- attached_obj_id: id,
- attached_obj_type: self.class)
- end
+ ToStdout.msg 'Destroying domain'
+ destroy!
+ ToStdout.msg "Checking if domain_name is auctionable: #{domain_name.auctionable?}"
+ domain_name.sell_at_auction if domain_name.auctionable?
+
+ ToStdout.msg 'Sending registrar notification'
+ registrar.notifications.create!(text: "#{I18n.t(:domain_deleted)}: #{name}",
+ attached_obj_id: id,
+ attached_obj_type: self.class)
else
discard
end
diff --git a/app/models/concerns/domain/transferable.rb b/app/models/concerns/domain/transferable.rb
index 9de2fff83..5400e9409 100644
--- a/app/models/concerns/domain/transferable.rb
+++ b/app/models/concerns/domain/transferable.rb
@@ -59,7 +59,7 @@ module Concerns::Domain::Transferable
copied_ids = []
domain_contacts.each do |dc|
contact = Contact.find(dc.contact_id)
- next if copied_ids.include?(contact.id) || contact.registrar == new_registrar
+ next if copied_ids.include?(uniq_contact_hash(dc)) || contact.registrar == new_registrar
if registrant_id_was == contact.id # registrant was copied previously, do not copy it again
oc = OpenStruct.new(id: registrant_id)
@@ -72,7 +72,11 @@ module Concerns::Domain::Transferable
else
dc.update(contact_id: oc.id)
end
- copied_ids << contact.id
+ copied_ids << uniq_contact_hash(dc)
end
end
+
+ def uniq_contact_hash(contact)
+ Digest::SHA1.hexdigest(contact.contact_id.to_s + contact.type)
+ end
end
diff --git a/app/models/contact_request.rb b/app/models/contact_request.rb
new file mode 100644
index 000000000..e6a5e9f7d
--- /dev/null
+++ b/app/models/contact_request.rb
@@ -0,0 +1,40 @@
+class ContactRequest < ApplicationRecord
+ establish_connection :"whois_#{Rails.env}"
+ self.table_name = 'contact_requests'
+
+ STATUS_NEW = 'new'.freeze
+ STATUS_CONFIRMED = 'confirmed'.freeze
+ STATUS_SENT = 'sent'.freeze
+ STATUSES = [STATUS_NEW, STATUS_CONFIRMED, STATUS_SENT].freeze
+
+ validates :whois_record_id, presence: true
+ validates :email, presence: true
+ validates :name, presence: true
+ validates :status, inclusion: { in: STATUSES }
+
+ 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_status(params)
+ self.status = params['status']
+ self.ip_address = params['ip']
+ save!
+ end
+
+ def self.create_random_secret
+ SecureRandom.hex(64)
+ end
+
+ def self.set_valid_to_24_hours_from_now
+ (Time.zone.now + 24.hours)
+ end
+end
diff --git a/app/models/dns/domain_name.rb b/app/models/dns/domain_name.rb
index c1af4d5e7..1e9cd6587 100644
--- a/app/models/dns/domain_name.rb
+++ b/app/models/dns/domain_name.rb
@@ -36,6 +36,7 @@ module DNS
auction = Auction.new
auction.domain = name
auction.start
+ ToStdout.msg "Created the auction: #{auction.inspect}"
update_whois_from_auction(auction)
end
@@ -100,7 +101,8 @@ module DNS
whois_record = Whois::Record.find_or_create_by!(name: name) do |record|
record.json = {}
end
-
+ ToStdout.msg "Starting to update WHOIS record #{whois_record.inspect}\n\n"\
+ "from auction #{auction.inspect}"
whois_record.update_from_auction(auction)
end
end
diff --git a/app/models/domain.rb b/app/models/domain.rb
index 88fa94fbc..53f0fa5b6 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -10,6 +10,7 @@ class Domain < ApplicationRecord
include Concerns::Domain::RegistryLockable
include Concerns::Domain::Releasable
include Concerns::Domain::Disputable
+ include Concerns::Domain::BulkUpdatable
attr_accessor :roles
@@ -78,7 +79,7 @@ class Domain < ApplicationRecord
true
end
- after_commit :update_whois_record, unless: -> { domain_name.at_auction? }
+ after_commit :update_whois_record
after_create :update_reserved_domains
def update_reserved_domains
diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb
index 50ebac065..0c0ed3d5f 100644
--- a/app/models/epp/contact.rb
+++ b/app/models/epp/contact.rb
@@ -42,17 +42,12 @@ class Epp::Contact < Contact
)
end
- def check_availability(codes)
+ def check_availability(codes, reg:)
codes = [codes] if codes.is_a?(String)
-
res = []
- codes.each do |x|
- contact = find_by_epp_code(x)
- if contact
- res << { code: contact.code, avail: 0, reason: 'in use' }
- else
- res << { code: x, avail: 1 }
- end
+ codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" }.map { |c| c.strip.upcase }.each do |x|
+ c = find_by_epp_code(x)
+ res << (c ? { code: c.code, avail: 0, reason: 'in use' } : { code: x, avail: 1 })
end
res
diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb
index d3a57df1f..ad96adc64 100644
--- a/app/models/epp/domain.rb
+++ b/app/models/epp/domain.rb
@@ -162,6 +162,9 @@ class Epp::Domain < Domain
at[:admin_domain_contacts_attributes] = admin_domain_contacts_attrs(frame, action)
at[:tech_domain_contacts_attributes] = tech_domain_contacts_attrs(frame, action)
+ check_for_same_contacts(at[:admin_domain_contacts_attributes], 'admin')
+ check_for_same_contacts(at[:tech_domain_contacts_attributes], 'tech')
+
pw = frame.css('authInfo > pw').text
at[:transfer_code] = pw if pw.present?
@@ -176,6 +179,11 @@ class Epp::Domain < Domain
at
end
+ def check_for_same_contacts(contacts, contact_type)
+ return unless contacts.uniq.count != contacts.count
+
+ add_epp_error('2306', contact_type, nil, %i[domain_contacts invalid])
+ end
# Adding legal doc to domain and
# if something goes wrong - raise Rollback error
@@ -312,6 +320,7 @@ class Epp::Domain < Domain
keys = []
return keys if frame.blank?
inf_data = DnsSecKeys.new(frame)
+ add_epp_error('2005', nil, nil, %i[dnskeys invalid]) if not_base64?(inf_data)
if action == 'rem' &&
frame.css('rem > all').first.try(:text) == 'true'
@@ -333,6 +342,16 @@ class Epp::Domain < Domain
errors.any? ? [] : keys
end
+ def not_base64?(inf_data)
+ inf_data.key_data.any? do |key|
+ value = key[:public_key]
+
+ !value.is_a?(String) || Base64.strict_encode64(Base64.strict_decode64(value)) != value
+ end
+ rescue ArgumentError
+ true
+ end
+
class DnsSecKeys
def initialize(frame)
@key_data = []
@@ -381,7 +400,7 @@ class Epp::Domain < Domain
def key_data_from(frame)
xm_copy frame, KEY_INTERFACE
- end
+ end
def ds_data_from(frame)
frame.css('dsData').each do |ds_data|
diff --git a/app/models/notification.rb b/app/models/notification.rb
index e83b2c9da..07e824367 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -25,6 +25,10 @@ class Notification < ApplicationRecord
''
end
+ def registry_lock?
+ text.include?('has been locked') || text.include?('has been unlocked')
+ end
+
private
def set_defaults
diff --git a/app/models/tech_domain_contact.rb b/app/models/tech_domain_contact.rb
index 92799061c..20f21b6ed 100644
--- a/app/models/tech_domain_contact.rb
+++ b/app/models/tech_domain_contact.rb
@@ -6,7 +6,7 @@ class TechDomainContact < DomainContact
tech_contacts = where(contact: current_contact)
tech_contacts.each do |tech_contact|
- if tech_contact.domain.discarded?
+ if tech_contact.domain.bulk_update_prohibited?
skipped_domains << tech_contact.domain.name
next
end
@@ -18,7 +18,6 @@ class TechDomainContact < DomainContact
skipped_domains << tech_contact.domain.name
end
end
-
[affected_domains.sort, skipped_domains.sort]
end
end
diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb
index 417633b12..38cee7b6b 100644
--- a/app/models/white_ip.rb
+++ b/app/models/white_ip.rb
@@ -4,8 +4,13 @@ class WhiteIp < ApplicationRecord
validate :valid_ipv4?
validate :valid_ipv6?
-
validate :validate_ipv4_and_ipv6
+ before_save :normalize_blank_values
+
+ def normalize_blank_values
+ %i[ipv4 ipv6].each { |c| self[c].present? || self[c] = nil }
+ end
+
def validate_ipv4_and_ipv6
return if ipv4.present? || ipv6.present?
errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present))
@@ -50,10 +55,10 @@ class WhiteIp < ApplicationRecord
def ids_including(ip)
ipv4 = ipv6 = []
if check_ip4(ip).present?
- ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) }
+ ipv4 = select { |white_ip| check_ip4(white_ip.ipv4) === check_ip4(ip) }
end
if check_ip6(ip).present?
- ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) }
+ ipv6 = select { |white_ip| check_ip6(white_ip.ipv6) === check_ip6(ip) }
end
(ipv4 + ipv6).pluck(:id).flatten.uniq
end
diff --git a/app/models/whois/record.rb b/app/models/whois/record.rb
index 1d827e22a..dc9cc2ba0 100644
--- a/app/models/whois/record.rb
+++ b/app/models/whois/record.rb
@@ -2,23 +2,34 @@ module Whois
class Record < Whois::Server
self.table_name = 'whois_records'
+ def self.without_auctions
+ ids = Whois::Record.all.select { |record| Auction.where(domain: record.name).blank? }
+ .pluck(:id)
+ Whois::Record.where(id: ids)
+ end
+
def self.disclaimer
Setting.registry_whois_disclaimer
end
+ # rubocop:disable Metrics/AbcSize
def update_from_auction(auction)
if auction.started?
update!(json: { name: auction.domain,
status: ['AtAuction'],
disclaimer: self.class.disclaimer })
+ ToStdout.msg "Updated from auction WHOIS record #{inspect}"
elsif auction.no_bids?
+ ToStdout.msg "Destroying WHOIS record #{inspect}"
destroy!
elsif auction.awaiting_payment? || auction.payment_received?
update!(json: { name: auction.domain,
status: ['PendingRegistration'],
disclaimer: self.class.disclaimer,
registration_deadline: auction.whois_deadline })
+ ToStdout.msg "Updated from auction WHOIS record #{inspect}"
end
end
+ # rubocop:enable Metrics/AbcSize
end
end
diff --git a/app/models/whois_record.rb b/app/models/whois_record.rb
index 3563b9630..19805d583 100644
--- a/app/models/whois_record.rb
+++ b/app/models/whois_record.rb
@@ -97,7 +97,7 @@ class WhoisRecord < ApplicationRecord
end
def destroy_whois_record
- Whois::Record.where(name: name).delete_all
+ Whois::Record.without_auctions.where(name: name).delete_all
end
private
diff --git a/app/views/admin/domains/_search_form.html.erb b/app/views/admin/domains/_search_form.html.erb
index bc317ea0b..1b1f50491 100644
--- a/app/views/admin/domains/_search_form.html.erb
+++ b/app/views/admin/domains/_search_form.html.erb
@@ -6,7 +6,7 @@
<%= f.search_field :name_matches, value: params[:q][:name_matches], class: 'form-control', placeholder: t(:name) %>
-
+
<%= f.label :registrant_ident, for: nil %>
<%= f.search_field :registrant_ident_eq, class: 'form-control', placeholder: t(:registrant_ident) %>
@@ -18,7 +18,7 @@
<%= f.search_field :contacts_ident_eq, class: 'form-control', placeholder: t(:contact_ident) %>
-
+
<%= f.label :nameserver_hostname, for: nil %>
<%= f.search_field :nameservers_hostname_eq, class: 'form-control', placeholder: t(:nameserver_hostname) %>
@@ -26,7 +26,7 @@
-
+
<%= f.label :registrar_name, for: nil %>
<%= f.select :registrar_id_eq, Registrar.all.map { |x| [x, x.id] }, { include_blank: true }, class: 'form-control selectize' %>
@@ -38,7 +38,7 @@
<%= f.search_field :valid_to_gteq, value: params[:q][:valid_to_gteq], class: 'form-control js-datepicker', placeholder: t(:valid_to_from) %>
-
+
<%= f.label :valid_to_until, for: nil %>
<%= f.search_field :valid_to_lteq, value: params[:q][:valid_to_lteq], class: 'form-control js-datepicker', placeholder: t(:valid_to_until) %>
@@ -46,7 +46,7 @@
-
+
<%= label_tag :status, nil, for: nil %>
<%= select_tag :statuses_contains, options_for_select(DomainStatus::STATUSES, params[:statuses_contains]), { multiple: true, class: 'form-control js-combobox' } %>
@@ -58,13 +58,17 @@
<%= text_field_tag :results_per_page, params[:results_per_page], class: 'form-control', placeholder: t(:results_per_page) %>
-
+
+ <%= link_to t('.download_csv_btn'), admin_domains_path(format: :csv, params: params.permit!),
+ "data-toggle" => "tooltip", "data-placement" => "bottom", "title" => t('.download_csv_btn'),
+ class: 'btn btn-default' %>
<%= link_to t('.reset_btn'), admin_domains_path, class: 'btn btn-default' %>
+
<% end %>
diff --git a/app/views/admin/repp_logs/show.haml b/app/views/admin/repp_logs/show.haml
index bbaae977f..9bb9ea52e 100644
--- a/app/views/admin/repp_logs/show.haml
+++ b/app/views/admin/repp_logs/show.haml
@@ -1,6 +1,6 @@
- content_for :actions do
= link_to(t(:back), :back, class: 'btn btn-primary')
-= render 'shared/title', name: t(:repp_log)
+= render 'shared/title', name: t('.title')
.row
.col-md-12
diff --git a/app/views/epp/poll/poll_req.xml.builder b/app/views/epp/poll/poll_req.xml.builder
index 664327dae..a58b082c5 100644
--- a/app/views/epp/poll/poll_req.xml.builder
+++ b/app/views/epp/poll/poll_req.xml.builder
@@ -15,12 +15,22 @@ xml.epp_head do
end if @object
end
- if @notification.action&.contact
- render(partial: 'epp/poll/action',
- locals: {
- builder: xml,
- action: @notification.action
- })
+ if @notification.action&.contact || @notification.registry_lock?
+ if @notification.registry_lock?
+ state = @notification.text.include?('unlocked') ? 'unlock' : 'lock'
+ xml.extension do
+ xml.tag!('changePoll:changeData',
+ 'xmlns:changePoll': 'https://epp.tld.ee/schema/changePoll-1.0.xsd') do
+ xml.tag!('changePoll:operation', state)
+ end
+ end
+ else
+ render(partial: 'epp/poll/action',
+ locals: {
+ builder: xml,
+ action: @notification.action,
+ })
+ end
end
render('epp/shared/trID', builder: xml)
diff --git a/app/views/registrar/bulk_change/_admin_contact_form.html.erb b/app/views/registrar/bulk_change/_admin_contact_form.html.erb
new file mode 100644
index 000000000..77734e872
--- /dev/null
+++ b/app/views/registrar/bulk_change/_admin_contact_form.html.erb
@@ -0,0 +1,65 @@
+<%= form_tag registrar_admin_contacts_path, method: :patch, class: 'form-horizontal' do %>
+ <% if @error %>
+
+ <%= @error %>
+
+ <% end %>
+
+
+
+
+
+
+
+
+<% end %>
+
+
diff --git a/app/views/registrar/bulk_change/_tech_contact_form.html.erb b/app/views/registrar/bulk_change/_tech_contact_form.html.erb
index 2848e3634..789db92ba 100644
--- a/app/views/registrar/bulk_change/_tech_contact_form.html.erb
+++ b/app/views/registrar/bulk_change/_tech_contact_form.html.erb
@@ -10,7 +10,7 @@
<%= label_tag :current_contact_id, t('.current_contact_id') %>
-
+
<%= text_field_tag :current_contact_id, params[:current_contact_id],
list: :contacts,
required: true,
@@ -24,7 +24,7 @@
<%= label_tag :new_contact_id, t('.new_contact_id') %>
-
+
+
+ <%= render 'admin_contact_form', available_contacts: available_contacts %>
+
+
<%= render 'nameserver_form' %>
diff --git a/config/application.rb b/config/application.rb
index a5fb17c9d..014c03269 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -36,8 +36,10 @@ module DomainNameRegistry
# Autoload all model subdirs
config.autoload_paths += Dir[Rails.root.join('app', 'models', '**/')]
+ config.autoload_paths += Dir[Rails.root.join('app', 'lib', '**/')]
config.autoload_paths += Dir[Rails.root.join('app', 'interactions', '**/')]
config.eager_load_paths << config.root.join('lib', 'validators')
+ config.eager_load_paths << config.root.join('app', 'lib')
config.watchable_dirs['lib'] = %i[rb]
config.active_record.schema_format = :sql
diff --git a/config/application.yml.sample b/config/application.yml.sample
index 5885c47a2..dd38e206c 100644
--- a/config/application.yml.sample
+++ b/config/application.yml.sample
@@ -87,8 +87,11 @@ sk_digi_doc_service_name: 'Testimine'
registrant_api_base_url:
registrant_api_auth_allowed_ips: '127.0.0.1, 0.0.0.0' #ips, separated with commas
-# Bounces API
-api_shared_key: testkey
+# Shared key for REST-WHOIS Bounces API incl. CERT
+rwhois_bounces_api_shared_key: testkey
+
+# Link to REST-WHOIS API
+rwhois_internal_api_shared_key: testkey
# Base URL (inc. https://) of REST registrant portal
# Leave blank to use internal registrant portal
diff --git a/config/initializers/aws_ses.rb b/config/initializers/aws_ses.rb
new file mode 100644
index 000000000..baa148e65
--- /dev/null
+++ b/config/initializers/aws_ses.rb
@@ -0,0 +1,4 @@
+Aws.config.update(
+ region: ENV['aws_default_region'],
+ credentials: Aws::Credentials.new(ENV['aws_access_key_id'], ENV['aws_secret_access_key'])
+)
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
index f2acc88e6..4f4e68288 100644
--- a/config/initializers/filter_parameter_logging.rb
+++ b/config/initializers/filter_parameter_logging.rb
@@ -1,9 +1,9 @@
Rails.application.configure do
config.filter_parameters += [:password, /^frame$/, /^nokogiri_frame$/, /^parsed_frame$/]
config.filter_parameters << lambda do |key, value|
- if key == 'raw_frame'
- value.to_s.gsub!(/pw>.+<\//, 'pw>[FILTERED]')
- value.to_s.gsub!(/
]+)>([^<])+<\/eis:legalDocument>/,
+ if key == 'raw_frame' && value.respond_to?(:gsub!)
+ value.gsub!(/pw>.+<\//, 'pw>[FILTERED]')
+ value.gsub!(/]+)>([^<])+<\/eis:legalDocument>/,
"[FILTERED]")
end
end
diff --git a/config/locales/admin/domains.en.yml b/config/locales/admin/domains.en.yml
index c6e96bb15..ce59294dd 100644
--- a/config/locales/admin/domains.en.yml
+++ b/config/locales/admin/domains.en.yml
@@ -11,6 +11,7 @@ en:
search_form:
reset_btn: Reset
+ download_csv_btn: CSV
form:
pending_delete: &pending_delete
diff --git a/config/locales/admin/repp_logs.en.yml b/config/locales/admin/repp_logs.en.yml
index 559ae234a..0a58fe7ba 100644
--- a/config/locales/admin/repp_logs.en.yml
+++ b/config/locales/admin/repp_logs.en.yml
@@ -4,3 +4,6 @@ en:
index:
title: REPP log
reset_btn: Reset
+ show:
+ title: REPP log
+
diff --git a/config/locales/notifications.en.yml b/config/locales/notifications.en.yml
index 1dff4a97c..d67fb5a5b 100644
--- a/config/locales/notifications.en.yml
+++ b/config/locales/notifications.en.yml
@@ -6,3 +6,5 @@ en:
It was associated with registrant %{old_registrant_code}
and contacts %{old_contacts_codes}.
contact_update: Contact %{contact} has been updated by registrant
+ registrar_locked: Domain %{domain_name} has been locked by registrant
+ registrar_unlocked: Domain %{domain_name} has been unlocked by registrant
diff --git a/config/locales/registrar/admin_contacts.en.yml b/config/locales/registrar/admin_contacts.en.yml
new file mode 100644
index 000000000..9265a6d10
--- /dev/null
+++ b/config/locales/registrar/admin_contacts.en.yml
@@ -0,0 +1,6 @@
+en:
+ registrar:
+ admin_contacts:
+ update:
+ replaced: Admin contacts have been successfully replaced.
+ replaced: Technical contacts have been successfully replaced.
diff --git a/config/locales/registrar/admin_contacts.yml b/config/locales/registrar/admin_contacts.yml
new file mode 100644
index 000000000..d258b4275
--- /dev/null
+++ b/config/locales/registrar/admin_contacts.yml
@@ -0,0 +1,11 @@
+en:
+ registrar:
+ admin_contacts:
+ update:
+ replaced: Admin contacts have been successfully replaced.
+ affected_domains: Affected domains
+ skipped_domains: Skipped domains
+ process_request:
+ affected_domains: Affected domains
+ skipped_domains: Skipped domains
+ replaced: Admin contacts have been successfully replaced.
diff --git a/config/locales/registrar/bulk_change.en.yml b/config/locales/registrar/bulk_change.en.yml
index 75becfada..d9f6ebbd2 100644
--- a/config/locales/registrar/bulk_change.en.yml
+++ b/config/locales/registrar/bulk_change.en.yml
@@ -4,6 +4,7 @@ en:
new:
header: Bulk change
technical_contact: Technical contact
+ admin_contact: Admin contact
nameserver: Nameserver
bulk_transfer: Bulk transfer
bulk_renew: Bulk renew
@@ -17,6 +18,19 @@ en:
Replace technical contact specified in "current contact ID" with the one in "new
contact ID" on any domain registered under this registrar
+ admin_contact_form:
+ current_contact_id: Current admin contact ID
+ new_contact_id: New admin contact ID
+ submit_btn: Replace admin contacts
+ help_btn: Toggle help
+ help: >-
+ Replace admin contact specified in "current contact ID" with the one in "new
+ contact ID" on any domain registered under this registrar. Contact idents must
+ be the same
+ comment: >-
+ Bulk admin change is only allowed in case of old and new contact are sharing identical
+ ident data ie for updating contact information.
+
nameserver_form:
ip_hint: One IP per line
replace_btn: Replace nameserver
@@ -38,3 +52,5 @@ en:
domain_ids: Domains for bulk renewal
current_balance: Current balance
period: Period
+ affected_domains: Affected domains
+ skipped_domains: Skipped domains
diff --git a/config/locales/registrar/tech_contacts.en.yml b/config/locales/registrar/tech_contacts.en.yml
index bf57f0cc7..b6e5d041b 100644
--- a/config/locales/registrar/tech_contacts.en.yml
+++ b/config/locales/registrar/tech_contacts.en.yml
@@ -5,3 +5,7 @@ en:
replaced: Technical contacts have been successfully replaced.
affected_domains: Affected domains
skipped_domains: Skipped domains
+ process_request:
+ affected_domains: Affected domains
+ skipped_domains: Skipped domains
+ replaced: Technical contacts have been successfully replaced.
diff --git a/config/routes.rb b/config/routes.rb
index 3042eced4..cfc88851c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -64,6 +64,7 @@ Rails.application.routes.draw do
get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ }
post 'transfer', to: 'domains#transfer'
patch 'contacts', to: 'domains/contacts#update'
+ patch 'admin_contacts', to: 'domains/admin_contacts#update'
post 'renew/bulk', to: 'domains/renews#bulk_renew'
end
end
@@ -91,6 +92,7 @@ Rails.application.routes.draw do
end
resources :auctions, only: %i[index show update], param: :uuid
+ resources :contact_requests, only: %i[create update], param: :id
resources :bounces, only: %i[create]
end
@@ -136,6 +138,7 @@ Rails.application.routes.draw do
resource :bulk_change, controller: :bulk_change, only: :new
post '/bulk_renew/new', to: 'bulk_change#bulk_renew', as: :bulk_renew
resource :tech_contacts, only: :update
+ resource :admin_contacts, only: :update
resource :nameservers, only: :update
resources :contacts, constraints: {:id => /[^\/]+(?=#{ ActionController::Renderers::RENDERERS.map{|e| "\\.#{e}\\z"}.join("|") })|[^\/]+/} do
member do
diff --git a/doc/repp/v1/admin_contacts.md b/doc/repp/v1/admin_contacts.md
new file mode 100644
index 000000000..3b78829d0
--- /dev/null
+++ b/doc/repp/v1/admin_contacts.md
@@ -0,0 +1,30 @@
+# Admin domain contacts
+
+## PATCH https://repp.internet.ee/v1/domains/admin_contacts
+Replaces admin domain contacts of the current registrar.
+
+### Example request
+```
+PATCH /repp/v1/domains/admin_contacts HTTP/1.1
+Accept: application/json
+Content-Type: application/json
+Authorization: Basic dGVzdDp0ZXN0dGVzdA==
+
+{
+ "current_contact_id": "ATSAA:749AA80F",
+ "new_contact_id": "ATSAA:E36957D7"
+}
+```
+### Example response
+```
+{
+ "code": 1000,
+ "message": "Command completed successfully",
+ "data": {
+ "affected_domains": [
+ "private.ee",
+ ],
+ "skipped_domains": []
+ }
+}
+```
diff --git a/doc/repp/v1/domain_contacts.md b/doc/repp/v1/domain_contacts.md
index 2e542bf81..8c3c9b9de 100644
--- a/doc/repp/v1/domain_contacts.md
+++ b/doc/repp/v1/domain_contacts.md
@@ -1,7 +1,7 @@
-# Domain contacts
+# Tech domain contacts
## PATCH https://repp.internet.ee/v1/domains/contacts
-Replaces all domain contacts of the current registrar.
+Replaces technical domain contacts of the current registrar.
### Example request
```
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
index b31489691..726767390 100644
--- a/test/application_system_test_case.rb
+++ b/test/application_system_test_case.rb
@@ -35,7 +35,6 @@ class JavaScriptApplicationSystemTestCase < ApplicationSystemTestCase
def setup
DatabaseCleaner.start
-
super
Capybara.current_driver = :chrome
diff --git a/test/fixtures/contact_requests.yml b/test/fixtures/contact_requests.yml
new file mode 100644
index 000000000..030a4d726
--- /dev/null
+++ b/test/fixtures/contact_requests.yml
@@ -0,0 +1,8 @@
+new:
+ whois_record_id: 1
+ email: aaa@bbb.com
+ name: Testname
+ status: new
+ secret: somesecret
+ valid_to: 2010-07-05
+
diff --git a/test/integration/admin_area/account_activities_test.rb b/test/integration/admin_area/account_activities_test.rb
new file mode 100644
index 000000000..306935269
--- /dev/null
+++ b/test/integration/admin_area/account_activities_test.rb
@@ -0,0 +1,39 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaAccountActivitiesIntegrationTest < ApplicationSystemTestCase
+ # /admin/account_activities
+ setup do
+ sign_in users(:admin)
+ @original_default_language = Setting.default_language
+ end
+
+ def test_show_account_activities_page
+ account_activities(:one).update(sum: "123.00")
+ visit admin_account_activities_path
+ assert_text 'Account activities'
+ end
+
+ def test_default_url_params
+ account_activities(:one).update(sum: "123.00")
+ visit admin_root_path
+ click_link_or_button 'Settings', match: :first
+ find(:xpath, "//ul/li/a[text()='Account activities']").click
+
+ assert has_current_path?(admin_account_activities_path(created_after: 'today'))
+ end
+
+ def test_download_account_activity
+ now = Time.zone.parse('2010-07-05 08:00')
+ travel_to now
+ account_activities(:one).update(sum: "123.00")
+
+ get admin_account_activities_path(format: :csv)
+
+ assert_response :ok
+ assert_equal "text/csv", response.headers['Content-Type']
+ assert_equal %(attachment; filename="account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv"; filename*=UTF-8''account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv),
+ response.headers['Content-Disposition']
+ assert_not_empty response.body
+ end
+end
diff --git a/test/integration/admin_area/admin_users_test.rb b/test/integration/admin_area/admin_users_test.rb
new file mode 100644
index 000000000..89b9edef9
--- /dev/null
+++ b/test/integration/admin_area/admin_users_test.rb
@@ -0,0 +1,101 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaAdminUsersIntegrationTest < JavaScriptApplicationSystemTestCase
+ include Devise::Test::IntegrationHelpers
+ include ActionView::Helpers::NumberHelper
+
+ setup do
+ WebMock.allow_net_connect!
+ @original_default_language = Setting.default_language
+ sign_in users(:admin)
+ end
+
+ def test_create_new_admin_user
+ createNewAdminUser(true)
+ end
+
+ def test_create_with_invalid_data_new_admin_user
+ createNewAdminUser(false)
+ end
+
+ def test_edit_successfully_exist_record
+ createNewAdminUser(true)
+
+ visit admin_admin_users_path
+ click_on 'test_user_name'
+
+ assert_text 'General'
+ click_on 'Edit'
+
+ fill_in 'Password', with: 'test_password'
+ fill_in 'Password confirmation', with: 'test_password'
+
+ click_on 'Save'
+ assert_text 'Record updated'
+ end
+
+ def test_edit_exist_record_with_invalid_data
+ createNewAdminUser(true)
+
+ visit admin_admin_users_path
+ click_on 'test_user_name'
+
+ assert_text 'General'
+ click_on 'Edit'
+
+ fill_in 'Password', with: 'test_password'
+ fill_in 'Password confirmation', with: 'test_password2'
+
+ click_on 'Save'
+ assert_text 'Failed to update record'
+ end
+
+ def test_delete_exist_record
+ createNewAdminUser(true)
+
+ visit admin_admin_users_path
+ click_on 'test_user_name'
+ assert_text 'General'
+ click_on 'Delete'
+
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Record deleted'
+ end
+
+ private
+
+ def createNewAdminUser(valid)
+ visit admin_admin_users_path
+ click_on 'New admin user'
+
+ fill_in 'Username', with: 'test_user_name'
+ # If valid=true creating valid user, if else, then with invalid data
+ if valid
+ fill_in 'Password', with: 'test_password'
+ fill_in 'Password confirmation', with: 'test_password'
+ else
+ fill_in 'Password', with: 'test_password'
+ fill_in 'Password confirmation', with: 'test_password2'
+ end
+ fill_in 'Identity code', with: '38903110313'
+ fill_in 'Email', with: 'oleg@tester.ee'
+
+ select 'Estonia', from: 'admin_user_country_code', match: :first
+
+ select_element = find(:xpath, "/html/body/div[2]/form/div[2]/div/div[7]/div[2]/div/div[1]")
+ select_element.click
+
+ option_element = find(:xpath, "/html/body/div[2]/form/div[2]/div/div[7]/div[2]/div/div[2]/div/div[1]")
+ option_element.click
+
+ click_on 'Save'
+
+ if valid
+ assert_text 'Record created'
+ else
+ assert_text 'Failed to create record'
+ end
+ end
+end
diff --git a/test/integration/admin_area/blocked_domains_test.rb b/test/integration/admin_area/blocked_domains_test.rb
new file mode 100644
index 000000000..7df9d30e7
--- /dev/null
+++ b/test/integration/admin_area/blocked_domains_test.rb
@@ -0,0 +1,68 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+
+# /admin/blocked_domains
+class AdminAreaBlockedDomainsIntegrationTest < JavaScriptApplicationSystemTestCase
+ setup do
+ WebMock.allow_net_connect!
+ sign_in users(:admin)
+ @domain = domains(:shop)
+ @blocked_domain = blocked_domains(:one)
+ end
+
+ def test_page_successfully_loaded
+ visit_admin_blocked_domains_path
+ end
+
+ def test_add_into_blocked_list
+ visit_admin_blocked_domains_path
+ add_domain_into_blocked_list(true)
+ end
+
+ def test_add_into_blocked_list_same_domain
+ visit_admin_blocked_domains_path
+ add_domain_into_blocked_list(true)
+ add_domain_into_blocked_list(false)
+ end
+
+ def test_delete_domain_from_blocked_list
+ visit_admin_blocked_domains_path
+ add_domain_into_blocked_list(true)
+
+ click_link_or_button 'Delete', match: :first
+
+ # Accept to delete in modal window
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Domain deleted!'
+ end
+
+ def test_find_blocked_domain_from_blocked_list
+ visit_admin_blocked_domains_path
+ add_domain_into_blocked_list(true)
+
+ fill_in 'Name', with: @domain.name
+ find(:xpath, "//span[@class='glyphicon glyphicon-search']").click
+
+ assert_text @domain.name
+ end
+
+ private
+
+ def visit_admin_blocked_domains_path
+ visit admin_blocked_domains_path
+ assert_text 'Blocked domains'
+ end
+
+ def add_domain_into_blocked_list(value)
+ click_on 'New blocked domain'
+ assert_text 'Add domain to blocked list'
+
+ fill_in 'Name', with: @domain.name
+ click_on 'Save'
+
+ return assert_text 'Domain added!' if value
+ return assert_text 'Failed to add domain!'
+ end
+end
diff --git a/test/integration/admin_area/certificates_test.rb b/test/integration/admin_area/certificates_test.rb
new file mode 100644
index 000000000..d2eec0bc4
--- /dev/null
+++ b/test/integration/admin_area/certificates_test.rb
@@ -0,0 +1,71 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaCertificatesIntegrationTest < JavaScriptApplicationSystemTestCase
+
+ setup do
+ WebMock.allow_net_connect!
+ sign_in users(:admin)
+
+ @apiuser = users(:api_bestnames)
+ @certificate = certificates(:api)
+ @certificate.update!(csr: "-----BEGIN CERTIFICATE REQUEST-----\nMIICszCCAZsCAQAwbjELMAkGA1UEBhMCRUUxFDASBgNVBAMMC2ZyZXNoYm94LmVl\nMRAwDgYDVQQHDAdUYWxsaW5uMREwDwYDVQQKDAhGcmVzaGJveDERMA8GA1UECAwI\nSGFyanVtYWExETAPBgNVBAsMCEZyZXNoYm94MIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA1VVESynZoZhIbe8s9zHkELZ/ZDCGiM2Q8IIGb1IOieT5U2mx\nIsVXz85USYsSQY9+4YdEXnupq9fShArT8pstS/VN6BnxdfAiYXc3UWWAuaYAdNGJ\nDr5Jf6uMt1wVnCgoDL7eJq9tWMwARC/viT81o92fgqHFHW0wEolfCmnpik9o0ACD\nFiWZ9IBIevmFqXtq25v9CY2cT9+eZW127WtJmOY/PKJhzh0QaEYHqXTHWOLZWpnp\nHH4elyJ2CrFulOZbHPkPNB9Nf4XQjzk1ffoH6e5IVys2VV5xwcTkF0jY5XTROVxX\nlR2FWqic8Q2pIhSks48+J6o1GtXGnTxv94lSDwIDAQABoAAwDQYJKoZIhvcNAQEL\nBQADggEBAEFcYmQvcAC8773eRTWBJJNoA4kRgoXDMYiiEHih5iJPVSxfidRwYDTF\nsP+ttNTUg3JocFHY75kuM9T2USh+gu/trRF0o4WWa+AbK3JbbdjdT1xOMn7XtfUU\nZ/f1XCS9YdHQFCA6nk4Z+TLWwYsgk7n490AQOiB213fa1UIe83qIfw/3GRqRUZ7U\nwIWEGsHED5WT69GyxjyKHcqGoV7uFnqFN0sQVKVTy/NFRVQvtBUspCbsOirdDRie\nAB2KbGHL+t1QrRF10szwCJDyk5aYlVhxvdI8zn010nrxHkiyQpDFFldDMLJl10BW\n2w9PGO061z+tntdRcKQGuEpnIr9U5Vs=\n-----END CERTIFICATE REQUEST-----\n")
+ end
+
+ def test_show_certificate_info
+ show_certificate_info
+ end
+
+ def test_destroy_certificate
+ show_certificate_info
+ find(:xpath, "//a[text()='Delete']").click
+
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Record deleted'
+ end
+
+ def test_download_csr
+ get download_csr_admin_api_user_certificate_path(api_user_id: @apiuser.id, id: @certificate.id)
+
+ assert_response :ok
+ assert_equal 'application/octet-stream', response.headers['Content-Type']
+ assert_equal "attachment; filename=\"test_bestnames.csr.pem\"; filename*=UTF-8''test_bestnames.csr.pem", response.headers['Content-Disposition']
+ assert_not_empty response.body
+ end
+
+ def test_download_crt
+ get download_crt_admin_api_user_certificate_path(api_user_id: @apiuser.id, id: @certificate.id)
+
+ assert_response :ok
+ assert_equal 'application/octet-stream', response.headers['Content-Type']
+ assert_equal "attachment; filename=\"test_bestnames.crt.pem\"; filename*=UTF-8''test_bestnames.crt.pem", response.headers['Content-Disposition']
+ assert_not_empty response.body
+ end
+
+ def test_failed_to_revoke_certificate
+ show_certificate_info
+
+ find(:xpath, "//a[text()='Revoke this certificate']").click
+ assert_text 'Failed to update record'
+ end
+
+ def test_new_api_user
+ visit new_admin_registrar_api_user_path(registrar_id: registrars(:bestnames).id)
+
+ fill_in 'Username', with: 'testapiuser'
+ fill_in 'Password', with: 'secretpassword'
+ fill_in 'Identity code', with: '60305062718'
+
+ click_on 'Create API user'
+
+ assert_text 'API user has been successfully created'
+ end
+
+ private
+
+ def show_certificate_info
+ visit admin_api_user_certificate_path(api_user_id: @apiuser.id, id: @certificate.id)
+ assert_text 'Certificates'
+ end
+end
diff --git a/test/integration/admin_area/epp_logs_test.rb b/test/integration/admin_area/epp_logs_test.rb
new file mode 100644
index 000000000..5ad514cb8
--- /dev/null
+++ b/test/integration/admin_area/epp_logs_test.rb
@@ -0,0 +1,52 @@
+# admin_epp_logs_path
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminEppLogsIntegrationTest < ApplicationSystemTestCase
+ setup do
+ sign_in users(:admin)
+ end
+
+ def test_visit_epp_logs_page
+ visit admin_epp_logs_path
+ assert_text 'EPP log'
+ end
+
+ def test_show_epp_log_page
+ visit admin_epp_logs_path
+ send_epp_request_hello
+ visit admin_epp_logs_path
+
+ find(:xpath, "//tbody/tr/td/a", match: :first).click
+ assert_text 'Details'
+ end
+
+ def test_dates_sort
+ Capybara.exact = true
+ visit admin_epp_logs_path
+ send_epp_request_hello
+ visit admin_epp_logs_path
+
+ find(:xpath, "//a[contains(text(), 'Created at')]", match: :first).click
+ find(:xpath, "//a[contains(text(), 'Created at')]", match: :first).click
+
+ epp_log_date = find(:xpath, "//table/tbody/tr/td[6]", match: :first).text(:all)
+ date_now = Date.today.to_s(:db)
+
+ assert_match /#{date_now}/, epp_log_date
+ end
+
+ private
+
+ def send_epp_request_hello
+ request_xml = <<-XML
+
+
+
+
+ XML
+
+ get epp_hello_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=non-existent' }
+ end
+end
diff --git a/test/integration/admin_area/invoices_test.rb b/test/integration/admin_area/invoices_test.rb
index 887f57212..2aa17201d 100644
--- a/test/integration/admin_area/invoices_test.rb
+++ b/test/integration/admin_area/invoices_test.rb
@@ -6,6 +6,27 @@ class AdminAreaInvoicesIntegrationTest < ApplicationIntegrationTest
sign_in users(:admin)
end
+ def test_create_new_invoice
+ visit new_admin_invoice_path
+
+ assert_text 'Create new invoice'
+ select 'Best Names', from: 'deposit_registrar_id', match: :first
+ fill_in 'Amount', with: '1000'
+ click_on 'Save'
+
+ assert_equal page.status_code, 200
+ end
+
+ def test_visit_list_of_invoices_pages
+ visit admin_invoices_path
+ assert_text 'Invoices'
+ end
+
+ def test_visit_invoice_page
+ visit admin_invoices_path(id: @invoice.id)
+ assert_text "Invoice no. #{@invoice.number}"
+ end
+
def test_downloads_invoice
assert_equal 1, @invoice.number
diff --git a/test/integration/admin_area/pending_delete_test.rb b/test/integration/admin_area/pending_delete_test.rb
new file mode 100644
index 000000000..737d12480
--- /dev/null
+++ b/test/integration/admin_area/pending_delete_test.rb
@@ -0,0 +1,60 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaPendingDeleteIntegrationTest < JavaScriptApplicationSystemTestCase
+ setup do
+ WebMock.allow_net_connect!
+ sign_in users(:admin)
+
+ @domain = domains(:shop)
+ @token = '123456'
+
+ @domain.update!(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: @token)
+ end
+
+ def test_accept_pending_delete
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Accept'
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Pending was successfully applied.'
+ end
+
+ def test_accept_pending_delete_no_success
+ @domain.update!(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: nil)
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Accept'
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Not success'
+ end
+
+ def test_reject_panding_delete
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Reject'
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Pending was successfully removed.'
+ end
+
+ def test_accept_pending_delete_no_success
+ @domain.update!(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: nil)
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Reject'
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Not success'
+ end
+end
diff --git a/test/integration/admin_area/pending_update_test.rb b/test/integration/admin_area/pending_update_test.rb
new file mode 100644
index 000000000..5d08e104b
--- /dev/null
+++ b/test/integration/admin_area/pending_update_test.rb
@@ -0,0 +1,96 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaPendingUpdateIntegrationTest < JavaScriptApplicationSystemTestCase
+
+ setup do
+ WebMock.allow_net_connect!
+ sign_in users(:admin)
+
+ @domain = domains(:hospital)
+
+ @new_registrant = contacts(:jack)
+ @user = users(:api_bestnames)
+ @token = '123456'
+
+ @domain.update!(statuses: [DomainStatus::PENDING_UPDATE],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: @token)
+ end
+
+ def test_accept_pending_update
+ pending_json = { new_registrant_id: @new_registrant.id,
+ new_registrant_name: @new_registrant.name,
+ new_registrant_email: @new_registrant.email,
+ current_user_id: @user.id }
+
+ @domain.update(pending_json: pending_json)
+ @domain.reload
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Accept'
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Pending was successfully applied.'
+ end
+
+ def test_accept_pending_update_no_success
+ @domain.update!(statuses: [DomainStatus::PENDING_UPDATE],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: nil)
+
+ pending_json = { new_registrant_id: @new_registrant.id,
+ new_registrant_name: @new_registrant.name,
+ new_registrant_email: @new_registrant.email,
+ current_user_id: @user.id,
+ }
+
+ @domain.update(pending_json: pending_json)
+ @domain.reload
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Accept'
+ page.driver.browser.switch_to.alert.accept
+ assert_text 'Not success'
+ end
+
+ def test_reject_panding_update
+ pending_json = { new_registrant_id: @new_registrant.id,
+ new_registrant_name: @new_registrant.name,
+ new_registrant_email: @new_registrant.email,
+ current_user_id: @user.id,
+ }
+
+ @domain.update(pending_json: pending_json)
+ @domain.reload
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Reject'
+ page.driver.browser.switch_to.alert.accept
+ assert_text 'Pending was successfully removed.'
+ end
+
+ def test_accept_pending_update_no_success
+ @domain.update!(statuses: [DomainStatus::PENDING_UPDATE],
+ registrant_verification_asked_at: Time.zone.now - 1.day,
+ registrant_verification_token: nil)
+
+ pending_json = { new_registrant_id: @new_registrant.id,
+ new_registrant_name: @new_registrant.name,
+ new_registrant_email: @new_registrant.email,
+ current_user_id: @user.id,
+ }
+
+ @domain.update(pending_json: pending_json)
+ @domain.reload
+
+ visit edit_admin_domain_path(id: @domain.id)
+
+ click_on 'Reject'
+ page.driver.browser.switch_to.alert.accept
+ assert_text 'Not success'
+ end
+end
diff --git a/test/integration/admin_area/registrars_test.rb b/test/integration/admin_area/registrars_test.rb
index d73496899..552650791 100644
--- a/test/integration/admin_area/registrars_test.rb
+++ b/test/integration/admin_area/registrars_test.rb
@@ -17,4 +17,4 @@ class AdminAreaRegistrarsIntegrationTest < ActionDispatch::IntegrationTest
assert_equal new_iban, @registrar.iban
end
-end
\ No newline at end of file
+end
diff --git a/test/integration/admin_area/repp_logs_test.rb b/test/integration/admin_area/repp_logs_test.rb
new file mode 100644
index 000000000..6630a6d57
--- /dev/null
+++ b/test/integration/admin_area/repp_logs_test.rb
@@ -0,0 +1,23 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaReppLogsIntegrationTest < ApplicationSystemTestCase
+ setup do
+ sign_in users(:admin)
+ end
+
+ def test_repp_logs_page
+ visit admin_repp_logs_path
+ assert_text 'REPP log'
+ end
+
+ def test_show_repp_log_page
+ visit admin_repp_logs_path
+ get repp_v1_contacts_path
+ visit admin_repp_logs_path
+
+ find(:xpath, "//tbody/tr/td/a", match: :first).click
+
+ assert_text 'REPP log'
+ end
+end
diff --git a/test/integration/admin_area/reserved_domains_test.rb b/test/integration/admin_area/reserved_domains_test.rb
new file mode 100644
index 000000000..c09c3723b
--- /dev/null
+++ b/test/integration/admin_area/reserved_domains_test.rb
@@ -0,0 +1,39 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaReservedDomainsIntegrationTest < JavaScriptApplicationSystemTestCase
+
+ setup do
+ WebMock.allow_net_connect!
+ @original_default_language = Setting.default_language
+ sign_in users(:admin)
+
+ @reserved_domain = reserved_domains(:one)
+ end
+
+ def test_remove_reserved_domain
+ visit admin_reserved_domains_path
+ click_link_or_button 'Delete', match: :first
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Domain deleted!'
+ end
+
+ def test_add_invalid_domain
+ visit admin_reserved_domains_path
+ click_on 'New reserved domain'
+ fill_in "Name", with: "@##@$"
+ click_on 'Save'
+
+ assert_text 'Failed to add domain!'
+ end
+
+ def test_update_reserved_domain
+ visit admin_reserved_domains_path
+ click_link_or_button 'Edit Pw', match: :first
+ fill_in 'Password', with: '12345678'
+ click_on 'Save'
+
+ assert_text 'Domain updated!'
+ end
+end
diff --git a/test/integration/admin_area/white_ips_test.rb b/test/integration/admin_area/white_ips_test.rb
new file mode 100644
index 000000000..499c86f57
--- /dev/null
+++ b/test/integration/admin_area/white_ips_test.rb
@@ -0,0 +1,94 @@
+require 'test_helper'
+require 'application_system_test_case'
+
+class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
+
+ setup do
+ WebMock.allow_net_connect!
+ sign_in users(:admin)
+
+ @registrar = registrars(:bestnames)
+ @white_ip = white_ips(:one)
+ end
+
+ def test_visit_new_whitelisted_ip_page
+ visit_new_whitelisted_ip_page
+ end
+
+ def test_create_new_whitelisted_ip
+ visit_new_whitelisted_ip_page
+ fill_in 'IPv4', with: "127.0.0.1"
+ fill_in 'IPv6', with: "::ffff:192.0.2.1"
+
+ find(:css, "#white_ip_interfaces_api").set(true)
+ find(:css, "#white_ip_interfaces_registrar").set(true)
+
+ click_on 'Save'
+
+ assert_text 'Record created'
+ end
+
+ def test_failed_to_create_new_whitelisted_ip
+ visit_new_whitelisted_ip_page
+ fill_in 'IPv4', with: "asdadadad.asd"
+
+ click_on 'Save'
+
+ assert_text 'Failed to create record'
+ end
+
+ def test_visit_edit_whitelisted_ip_page
+ visit_edit_whitelisted_ip_page
+ end
+
+ def test_update_whitelisted_ip
+ visit_info_whitelisted_ip_page
+ click_on 'Edit'
+
+ fill_in 'IPv4', with: "127.0.0.2"
+ find(:css, "#white_ip_interfaces_api").set(false)
+ click_on 'Save'
+
+ assert_text 'Record updated'
+ end
+
+ def test_failed_to_update_whitelisted_ip
+ visit_info_whitelisted_ip_page
+ click_on 'Edit'
+ fill_in 'IPv4', with: "asdadad#"
+
+ click_on 'Save'
+
+ assert_text 'Failed to update record'
+ end
+
+ def test_visit_info_whitelisted_ip_page
+ visit_info_whitelisted_ip_page
+ end
+
+ def test_delete_whitelisted_ip
+ visit_info_whitelisted_ip_page
+ click_on 'Delete'
+
+ page.driver.browser.switch_to.alert.accept
+
+ assert_text 'Record deleted'
+ end
+
+ private
+
+ def visit_new_whitelisted_ip_page
+ visit new_admin_registrar_white_ip_path(registrar_id: @registrar.id)
+ assert_text 'New whitelisted IP'
+ end
+
+ def visit_edit_whitelisted_ip_page
+ visit edit_admin_registrar_white_ip_path(registrar_id: @registrar.id, id: @white_ip.id)
+ assert_text 'Edit white IP'
+ end
+
+ def visit_info_whitelisted_ip_page
+ visit admin_registrar_white_ip_path(registrar_id: @registrar.id, id: @white_ip.id)
+ assert_text 'White IP'
+ end
+end
diff --git a/test/integration/api/domain_admin_contacts_test.rb b/test/integration/api/domain_admin_contacts_test.rb
new file mode 100644
index 000000000..cd5b92865
--- /dev/null
+++ b/test/integration/api/domain_admin_contacts_test.rb
@@ -0,0 +1,153 @@
+require 'test_helper'
+
+class APIDomainAdminContactsTest < ApplicationIntegrationTest
+ setup do
+ @admin_current = domains(:shop).admin_contacts.find_by(code: 'jane-001')
+ domain = domains(:airport)
+ domain.admin_contacts << @admin_current
+ @admin_new = contacts(:william)
+
+ @admin_new.update(ident: @admin_current.ident,
+ ident_type: @admin_current.ident_type,
+ ident_country_code: @admin_current.ident_country_code)
+ end
+
+ def test_replace_all_admin_contacts_when_ident_data_doesnt_match
+ @admin_new.update(ident: '777' ,
+ ident_type: 'priv',
+ ident_country_code: 'LV')
+
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :bad_request
+ assert_equal ({ code: 2304, message: 'Admin contacts must be identical', data: {} }),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_replace_all_admin_contacts_of_the_current_registrar
+ assert @admin_new.identical_to?(@admin_current)
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_nil domains(:shop).admin_contacts.find_by(code: @admin_current.code)
+ assert domains(:shop).admin_contacts.find_by(code: @admin_new.code)
+ assert domains(:airport).admin_contacts.find_by(code: @admin_new.code)
+ end
+
+ def test_skip_discarded_domains
+ domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
+
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert domains(:airport).admin_contacts.find_by(code: @admin_current.code)
+ end
+
+ def test_return_affected_domains_in_alphabetical_order
+ domain = domains(:airport)
+ domain.admin_contacts = [@admin_current]
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :ok
+ assert_equal ({ code: 1000, message: 'Command completed successfully', data: { affected_domains: %w[airport.test shop.test],
+ skipped_domains: [] }}),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_return_skipped_domains_in_alphabetical_order
+ domains(:shop).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
+ domains(:airport).update!(statuses: [DomainStatus::DELETE_CANDIDATE])
+
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :ok
+ assert_equal %w[airport.test shop.test], JSON.parse(response.body,
+ symbolize_names: true)[:data][:skipped_domains]
+ end
+
+ def test_keep_other_admin_contacts_intact
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert domains(:airport).admin_contacts.find_by(code: 'john-001')
+ end
+
+ def test_keep_tech_contacts_intact
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert domains(:airport).tech_contacts.find_by(code: 'william-001')
+ end
+
+ def test_restrict_contacts_to_the_current_registrar
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: 'william-002' },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :not_found
+ assert_equal ({ code: 2303, message: 'Object does not exist' }),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_non_existent_current_contact
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: 'non-existent',
+ new_contact_id: @admin_new.code},
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+ assert_response :not_found
+ assert_equal ({ code: 2303, message: 'Object does not exist' }),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_non_existent_new_contact
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: 'non-existent' },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+ assert_response :not_found
+ assert_equal ({code: 2303, message: 'Object does not exist'}),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_disallow_invalid_new_contact
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: 'invalid' },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+ assert_response :bad_request
+ assert_equal ({ code: 2304, message: 'New contact must be valid', data: {} }),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ def test_admin_bulk_changed_when_domain_update_prohibited
+ domains(:shop).update!(statuses: [DomainStatus::SERVER_UPDATE_PROHIBITED])
+ domains(:airport).admin_contacts = [@admin_current]
+
+ shop_admin_contact = Contact.find_by(code: 'jane-001')
+ assert domains(:shop).admin_contacts.include?(shop_admin_contact)
+
+ patch '/repp/v1/domains/admin_contacts', params: { current_contact_id: @admin_current.code,
+ new_contact_id: @admin_new.code },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :ok
+ assert_equal ({ code: 1000,
+ message: 'Command completed successfully',
+ data: { affected_domains: ["airport.test"],
+ skipped_domains: ["shop.test"] }}),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ private
+
+ def http_auth_key
+ ActionController::HttpAuthentication::Basic.encode_credentials('test_bestnames', 'testtest')
+ end
+end
diff --git a/test/integration/api/domain_contacts_test.rb b/test/integration/api/domain_contacts_test.rb
index 6704739d1..efd7032b4 100644
--- a/test/integration/api/domain_contacts_test.rb
+++ b/test/integration/api/domain_contacts_test.rb
@@ -107,6 +107,24 @@ class APIDomainContactsTest < ApplicationIntegrationTest
JSON.parse(response.body, symbolize_names: true)
end
+ def test_tech_bulk_changed_when_domain_update_prohibited
+ domains(:shop).update!(statuses: [DomainStatus::SERVER_UPDATE_PROHIBITED])
+
+ shop_tech_contact = Contact.find_by(code: 'william-001')
+ assert domains(:shop).tech_contacts.include?(shop_tech_contact)
+
+ patch '/repp/v1/domains/contacts', params: { current_contact_id: 'william-001',
+ new_contact_id: 'john-001' },
+ headers: { 'HTTP_AUTHORIZATION' => http_auth_key }
+
+ assert_response :ok
+ assert_equal ({ code: 1000,
+ message: 'Command completed successfully',
+ data: { affected_domains: ["airport.test"],
+ skipped_domains: ["shop.test"] }}),
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
private
def http_auth_key
diff --git a/test/integration/api/v1/bounces/create_test.rb b/test/integration/api/v1/bounces/create_test.rb
index 899b6c5c7..0d3dc65d7 100644
--- a/test/integration/api/v1/bounces/create_test.rb
+++ b/test/integration/api/v1/bounces/create_test.rb
@@ -2,7 +2,7 @@ require 'test_helper'
class BouncesApiV1CreateTest < ActionDispatch::IntegrationTest
def setup
- @api_key = "Basic #{ENV['api_shared_key']}"
+ @api_key = "Basic #{ENV['rwhois_bounces_api_shared_key']}"
@headers = { "Authorization": "#{@api_key}" }
@json_body = { "data": valid_bounce_request }.as_json
end
diff --git a/test/integration/api/v1/contact_requests_test.rb b/test/integration/api/v1/contact_requests_test.rb
new file mode 100644
index 000000000..c9e2ac9bd
--- /dev/null
+++ b/test/integration/api/v1/contact_requests_test.rb
@@ -0,0 +1,68 @@
+require 'test_helper'
+
+class ApiV1ContactRequestTest < ActionDispatch::IntegrationTest
+ def setup
+ @api_key = "Basic #{ENV['rwhois_internal_api_shared_key']}"
+ @headers = { "Authorization": "#{@api_key}" }
+ @json_create = { "contact_request": valid_contact_request_create }.as_json
+ @json_update = { "contact_request": valid_contact_request_update }.as_json
+ @contact_request = contact_requests(:new)
+ end
+
+ def test_authorizes_api_request
+ post api_v1_contact_requests_path, params: @json_create, headers: @headers
+ assert_response :created
+
+ invalid_headers = { "Authorization": "Basic invalid_api_key" }
+ post api_v1_contact_requests_path, params: @json_create, headers: invalid_headers
+ assert_response :unauthorized
+ end
+
+ def test_saves_new_contact_request
+ request_body = @json_create.dup
+ random_mail = "#{rand(10000..99999)}@registry.test"
+ request_body['contact_request']['email'] = random_mail
+
+ post api_v1_contact_requests_path, params: request_body, headers: @headers
+ assert_response :created
+
+ contact_request = ContactRequest.last
+ assert_equal contact_request.email, random_mail
+ assert ContactRequest::STATUS_NEW, contact_request.status
+ end
+
+ def test_updates_existing_contact_request
+ request_body = @json_update.dup
+
+ put api_v1_contact_request_path(@contact_request.id), params: request_body, headers: @headers
+ assert_response :ok
+
+ @contact_request.reload
+ assert ContactRequest::STATUS_CONFIRMED, @contact_request.status
+ end
+
+ def test_not_updates_if_status_error
+ request_body = @json_update.dup
+ request_body['contact_request']['status'] = 'some_error_status'
+
+ put api_v1_contact_request_path(@contact_request.id), params: request_body, headers: @headers
+ assert_response 400
+
+ @contact_request.reload
+ assert ContactRequest::STATUS_NEW, @contact_request.status
+ end
+
+ def valid_contact_request_create
+ {
+ "email": "aaa@bbb.com",
+ "whois_record_id": "1",
+ "name": "test"
+ }.as_json
+ end
+
+ def valid_contact_request_update
+ {
+ "status": "#{ContactRequest::STATUS_CONFIRMED}",
+ }.as_json
+ end
+end
diff --git a/test/integration/api/v1/registrant/contacts/list_test.rb b/test/integration/api/v1/registrant/contacts/list_test.rb
index 2389019f1..33e8b67eb 100644
--- a/test/integration/api/v1/registrant/contacts/list_test.rb
+++ b/test/integration/api/v1/registrant/contacts/list_test.rb
@@ -1,5 +1,6 @@
require 'test_helper'
require 'auth_token/auth_token_creator'
+require 'json'
CompanyRegisterClientStub = Struct.new(:any_method) do
def representation_rights(citizen_personal_code:, citizen_country_code:)
@@ -55,6 +56,42 @@ class RegistrantApiV1ContactListTest < ActionDispatch::IntegrationTest
assert_equal '1234', response_json.first[:ident][:code]
end
+ def test_out_of_range_limit
+ get api_v1_registrant_contacts_path + "?limit=300", as: :json, headers: { 'HTTP_AUTHORIZATION' => auth_token }
+ response_json = JSON.parse(response.body, symbolize_names: true)
+
+ text_response = JSON.pretty_generate(response_json[:errors][0][:limit][0])
+
+ assert_equal text_response, '"parameter is out of range"'
+ end
+
+ def test_negative_offset
+ get api_v1_registrant_contacts_path + "?offset=-300", as: :json, headers: { 'HTTP_AUTHORIZATION' => auth_token }
+ response_json = JSON.parse(response.body, symbolize_names: true)
+
+ text_response = JSON.pretty_generate(response_json[:errors][0][:offset][0])
+
+ assert_equal text_response, '"parameter is out of range"'
+ end
+
+ def test_show_valid_contact
+ get api_v1_registrant_contacts_path + "/eb2f2766-b44c-4e14-9f16-32ab1a7cb957", as: :json, headers: { 'HTTP_AUTHORIZATION' => auth_token }
+ response_json = JSON.parse(response.body, symbolize_names: true)
+
+ text_response = response_json[:name]
+
+ assert_equal @contact[:name], text_response
+ end
+
+ def test_show_invalid_contact
+ get api_v1_registrant_contacts_path + "/435", as: :json, headers: { 'HTTP_AUTHORIZATION' => auth_token }
+ response_json = JSON.parse(response.body, symbolize_names: true)
+
+ text_response = response_json[:errors][0][:base][0]
+
+ assert_equal text_response, 'Contact not found'
+ end
+
private
def delete_direct_contact
diff --git a/test/integration/api/v1/registrant/contacts/update_test.rb b/test/integration/api/v1/registrant/contacts/update_test.rb
index 4ddf8b0ff..c1eaa005c 100644
--- a/test/integration/api/v1/registrant/contacts/update_test.rb
+++ b/test/integration/api/v1/registrant/contacts/update_test.rb
@@ -4,11 +4,12 @@ require 'auth_token/auth_token_creator'
class RegistrantApiV1ContactUpdateTest < ActionDispatch::IntegrationTest
setup do
@contact = contacts(:john)
+ @contact_org = contacts(:acme_ltd)
@original_address_processing = Setting.address_processing
@original_fax_enabled_setting = ENV['fax_enabled']
-
@user = users(:registrant)
+
end
teardown do
@@ -90,6 +91,32 @@ class RegistrantApiV1ContactUpdateTest < ActionDispatch::IntegrationTest
@contact.address
end
+ def test_update_address_when_enabled_without_address_params
+ Setting.address_processing = true
+
+ patch api_v1_registrant_contact_path(@contact.uuid), params: { address: { } },
+ as: :json,
+ headers: { 'HTTP_AUTHORIZATION' => auth_token }
+
+ assert_response :bad_request
+ @contact.reload
+ assert_equal Contact::Address.new(nil, nil, nil, nil, nil),
+ @contact.address
+ end
+
+ def test_update_address_when_enabled_without_address_params
+ Setting.address_processing = true
+
+ patch api_v1_registrant_contact_path(@contact.uuid), params: { },
+ as: :json,
+ headers: { 'HTTP_AUTHORIZATION' => auth_token }
+
+ assert_response :bad_request
+ @contact.reload
+ assert_equal Contact::Address.new(nil, nil, nil, nil, nil),
+ @contact.address
+ end
+
def test_address_is_optional_when_enabled
Setting.address_processing = true
@contact.update!(street: 'any', zip: 'any', city: 'any', state: 'any', country_code: 'US')
@@ -211,6 +238,21 @@ class RegistrantApiV1ContactUpdateTest < ActionDispatch::IntegrationTest
symbolize_names: true)
end
+ def test_org_disclosed_attributes
+ patch api_v1_registrant_contact_path(@contact_org.uuid), params: { disclosed_attributes: ["some_attr"] },
+ as: :json,
+ headers: { 'HTTP_AUTHORIZATION' => auth_token }
+
+ assert_response :bad_request
+
+ err_msg = "Legal person's data is visible by default and cannot be concealed. Please remove this parameter."
+
+ response_json = JSON.parse(response.body, symbolize_names: true)
+ response_msg = response_json[:errors][0][:disclosed_attributes][0]
+
+ assert_equal err_msg, response_msg
+ end
+
def test_unmanaged_contact_cannot_be_updated
assert_equal 'US-1234', @user.registrant_ident
@contact.update!(ident: '12345')
diff --git a/test/integration/epp/contact/check/base_test.rb b/test/integration/epp/contact/check/base_test.rb
index 528d69d86..8f7e3df16 100644
--- a/test/integration/epp/contact/check/base_test.rb
+++ b/test/integration/epp/contact/check/base_test.rb
@@ -26,7 +26,7 @@ class EppContactCheckBaseTest < EppTestCase
response_xml = Nokogiri::XML(response.body)
assert_epp_response :completed_successfully
- assert_equal 'john-001', response_xml.at_xpath('//contact:id', contact: xml_schema).text
+ assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text
end
def test_contact_is_available
@@ -52,7 +52,8 @@ class EppContactCheckBaseTest < EppTestCase
end
def test_contact_is_unavailable
- assert_equal 'john-001', @contact.code
+ @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase)
+ assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase
request_xml = <<-XML
@@ -98,9 +99,61 @@ class EppContactCheckBaseTest < EppTestCase
assert_equal 3, response_xml.xpath('//contact:cd', contact: xml_schema).size
end
+ def test_check_contact_with_prefix
+ @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase)
+ assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase
+
+ request_xml = <<-XML
+
+
+
+
+
+ BESTNAMES:JOHN-001
+
+
+
+
+ XML
+
+ post epp_check_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+
+ response_xml = Nokogiri::XML(response.body)
+ assert_epp_response :completed_successfully
+ assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text
+ assert_equal 'in use', response_xml.at_xpath('//contact:reason', contact: xml_schema).text
+ end
+
+ def test_check_contact_without_prefix
+ @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase)
+ assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase
+
+ request_xml = <<-XML
+
+
+
+
+
+ JOHN-001
+
+
+
+
+ XML
+
+ post epp_check_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+
+ response_xml = Nokogiri::XML(response.body)
+ assert_epp_response :completed_successfully
+ assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text
+ assert_equal 'in use', response_xml.at_xpath('//contact:reason', contact: xml_schema).text
+ end
+
private
def xml_schema
'https://epp.tld.ee/schema/contact-ee-1.1.xsd'
end
-end
\ No newline at end of file
+end
diff --git a/test/integration/epp/contact/info/base_test.rb b/test/integration/epp/contact/info/base_test.rb
index 4e4a9190e..3edb6d461 100644
--- a/test/integration/epp/contact/info/base_test.rb
+++ b/test/integration/epp/contact/info/base_test.rb
@@ -44,6 +44,58 @@ class EppContactInfoBaseTest < EppTestCase
contact: xml_schema).text
end
+ def test_get_info_about_contact_with_prefix
+ @contact.update_columns(code: 'TEST:JOHN-001')
+ assert @contact.code, 'TEST:JOHN-001'
+
+ request_xml = <<-XML
+
+
+
+
+
+ TEST:JOHN-001
+
+
+
+
+ XML
+
+ post epp_info_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+
+ response_xml = Nokogiri::XML(response.body)
+ assert_epp_response :completed_successfully
+ assert_equal 'TEST:JOHN-001', response_xml.at_xpath('//contact:id', contact: xml_schema).text
+ assert_equal '+555.555', response_xml.at_xpath('//contact:voice', contact: xml_schema).text
+ end
+
+ def test_get_info_about_contact_without_prefix
+ @contact.update_columns(code: "#{@contact.registrar.code}:JOHN-001".upcase)
+ assert @contact.code, "#{@contact.registrar.code}:JOHN-001".upcase
+
+ request_xml = <<-XML
+
+
+
+
+
+ JOHN-001
+
+
+
+
+ XML
+
+ post epp_info_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+
+ response_xml = Nokogiri::XML(response.body)
+ assert_epp_response :completed_successfully
+ assert_equal "#{@contact.registrar.code}:JOHN-001".upcase, response_xml.at_xpath('//contact:id', contact: xml_schema).text
+ assert_equal '+555.555', response_xml.at_xpath('//contact:voice', contact: xml_schema).text
+ end
+
def test_hides_password_and_name_when_current_registrar_is_not_sponsoring
non_sponsoring_registrar = registrars(:goodnames)
@contact.update!(registrar: non_sponsoring_registrar)
diff --git a/test/integration/epp/domain/create/base_test.rb b/test/integration/epp/domain/create/base_test.rb
index 9d817524d..35ef38179 100644
--- a/test/integration/epp/domain/create/base_test.rb
+++ b/test/integration/epp/domain/create/base_test.rb
@@ -2,6 +2,51 @@ require 'test_helper'
class EppDomainCreateBaseTest < EppTestCase
+ def test_illegal_chars_in_dns_key
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+
+ pub_key = "AwEAAddt2AkLf\n
+ \n
+ YGKgiEZB5SmIF8E\n
+ vrjxNMH6HtxW\rEA4RJ9Ao6LCWheg8"
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+
+
+
+
+
+ 257
+ 3
+ 8
+ #{pub_key}
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_syntax_error
+ end
+
+
def test_not_registers_domain_without_legaldoc
now = Time.zone.parse('2010-07-05')
travel_to now
@@ -31,6 +76,230 @@ class EppDomainCreateBaseTest < EppTestCase
assert_epp_response :required_parameter_missing
end
+ def test_create_domain_with_unique_contact
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contacts(:jane).code}
+ #{contacts(:william).code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+ assert_epp_response :completed_successfully
+ end
+
+
+ def test_create_domain_with_array_of_not_unique_admins_and_techs
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contact.code}
+ #{contact.code}
+ #{contact.code}
+ #{contact.code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_policy_error
+ end
+
+ def test_create_domain_with_array_of_not_unique_admins
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contact.code}
+ #{contact.code}
+ #{contact.code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_policy_error
+ end
+
+ def test_create_domain_with_array_of_not_unique_techs
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contact.code}
+ #{contact.code}
+ #{contact.code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_policy_error
+ end
+
+ def test_create_domain_with_array_of_not_unique_admin_but_tech_another_one
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+ contact_two = contacts(:william)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contact.code}
+ #{contact.code}
+ #{contact_two.code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_policy_error
+ end
+
+ def test_create_domain_with_array_of_not_unique_techs_but_admin_another_one
+ now = Time.zone.parse('2010-07-05')
+ travel_to now
+ name = "new.#{dns_zones(:one).origin}"
+ contact = contacts(:john)
+ registrant = contact.becomes(Registrant)
+ contact_two = contacts(:william)
+
+ request_xml = <<-XML
+
+
+
+
+
+ #{name}
+ #{registrant.code}
+ #{contact_two.code}
+ #{contact.code}
+ #{contact.code}
+
+
+
+
+ #{'test' * 2000}
+
+
+
+
+ XML
+
+ assert_no_difference 'Domain.count' do
+ post epp_create_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
+ end
+
+ assert_epp_response :parameter_value_policy_error
+ end
+
def test_registers_new_domain_with_required_attributes
now = Time.zone.parse('2010-07-05')
travel_to now
diff --git a/test/integration/epp/domain/transfer/request_test.rb b/test/integration/epp/domain/transfer/request_test.rb
index 1c3614421..273a9f490 100644
--- a/test/integration/epp/domain/transfer/request_test.rb
+++ b/test/integration/epp/domain/transfer/request_test.rb
@@ -3,6 +3,7 @@ require 'test_helper'
class EppDomainTransferRequestTest < EppTestCase
def setup
@domain = domains(:shop)
+ @contact = contacts(:jane)
@new_registrar = registrars(:goodnames)
@original_transfer_wait_time = Setting.transfer_wait_time
Setting.transfer_wait_time = 0
@@ -12,6 +13,95 @@ class EppDomainTransferRequestTest < EppTestCase
Setting.transfer_wait_time = @original_transfer_wait_time
end
+ def test_transfer_domain_with_contacts_if_registrant_and_tech_are_shared
+ @domain.tech_domain_contacts[0].update!(contact_id: @domain.registrant.id)
+
+ @domain.tech_domain_contacts[1].delete
+ @domain.reload
+
+ post epp_transfer_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_goodnames' }
+
+ assert_epp_response :completed_successfully
+
+ @domain.reload
+
+ tech = Contact.find_by(id: @domain.tech_domain_contacts[0].contact_id)
+
+ assert_equal @domain.contacts.where(original_id: @domain.registrant.original_id).count, 1
+ assert_equal tech.registrar_id, @domain.registrar.id
+ end
+
+ def test_transfer_domain_with_contacts_if_registrant_and_admin_are_shared
+ @domain.admin_domain_contacts[0].update!(contact_id: @domain.registrant.id)
+ @domain.tech_domain_contacts[0].update!(contact_id: @contact.id)
+
+ @domain.tech_domain_contacts[1].delete
+ @domain.reload
+
+ post epp_transfer_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_goodnames' }
+
+ assert_epp_response :completed_successfully
+
+ @domain.reload
+
+ admin = Contact.find_by(id: @domain.admin_domain_contacts[0].contact_id)
+
+ assert_equal @domain.contacts.where(original_id: @domain.registrant.original_id).count, 1
+ assert_equal admin.registrar_id, @domain.registrar.id
+ end
+
+ def test_transfer_domain_with_contacts_if_admin_and_tech_are_shared
+ @domain.admin_domain_contacts[0].update!(contact_id: @contact.id)
+ @domain.tech_domain_contacts[0].update!(contact_id: @contact.id)
+
+ @domain.tech_domain_contacts[1].delete
+ @domain.reload
+
+ post epp_transfer_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_goodnames' }
+
+ assert_epp_response :completed_successfully
+
+ @domain.reload
+
+ admin = Contact.find_by(id: @domain.admin_domain_contacts[0].contact_id)
+ tech = Contact.find_by(id: @domain.tech_domain_contacts[0].contact_id)
+
+ result_hash = @domain.contacts.pluck(:original_id).group_by(&:itself).transform_values(&:count)
+ assert result_hash[admin.original_id], 2
+
+ assert_equal admin.registrar_id, @domain.registrar.id
+ assert_equal tech.registrar_id, @domain.registrar.id
+ end
+
+ def test_transfer_domain_with_contacts_if_admin_and_tech_and_registrant_are_shared
+ @domain.tech_domain_contacts[0].update!(contact_id: @domain.registrant.id)
+ @domain.admin_domain_contacts[0].update!(contact_id: @domain.registrant.id)
+
+ @domain.tech_domain_contacts[1].delete
+ @domain.reload
+
+ post epp_transfer_path, params: { frame: request_xml },
+ headers: { 'HTTP_COOKIE' => 'session=api_goodnames' }
+
+ assert_epp_response :completed_successfully
+
+ @domain.reload
+
+ admin = Contact.find_by(id: @domain.admin_domain_contacts[0].contact_id)
+ tech = Contact.find_by(id: @domain.tech_domain_contacts[0].contact_id)
+
+ assert_equal @domain.contacts.where(original_id: @domain.registrant.original_id).count, 2
+
+ result_hash = @domain.contacts.pluck(:original_id).group_by(&:itself).transform_values(&:count)
+ assert result_hash[@domain.registrant.original_id], 2
+
+ assert_equal admin.registrar_id, @domain.registrar.id
+ assert_equal tech.registrar_id, @domain.registrar.id
+ end
+
def test_transfers_domain_at_once
post epp_transfer_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_goodnames' }
diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb
index ded0d3d8a..d2d3a3252 100644
--- a/test/jobs/domain_update_confirm_job_test.rb
+++ b/test/jobs/domain_update_confirm_job_test.rb
@@ -1,5 +1,4 @@
require "test_helper"
-
class DomainUpdateConfirmJobTest < ActiveSupport::TestCase
def setup
super
@@ -19,6 +18,22 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase
super
end
+ def test_registrant_locked_domain
+ refute @domain.locked_by_registrant?
+ @domain.apply_registry_lock
+ assert @domain.locked_by_registrant?
+ assert_equal(@domain.registrar.notifications.last.text, "Domain #{@domain.name} has been locked by registrant")
+ end
+
+ def test_registrant_unlocked_domain
+ refute @domain.locked_by_registrant?
+ @domain.apply_registry_lock
+ assert @domain.locked_by_registrant?
+ @domain.remove_registry_lock
+ refute @domain.locked_by_registrant?
+ assert_equal(@domain.registrar.notifications.last.text, "Domain #{@domain.name} has been unlocked by registrant")
+ end
+
def test_rejected_registrant_verification_notifies_registrar
DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED)
diff --git a/test/models/domain/releasable/auctionable_test.rb b/test/models/domain/releasable/auctionable_test.rb
index de3ac0ff6..d24f46913 100644
--- a/test/models/domain/releasable/auctionable_test.rb
+++ b/test/models/domain/releasable/auctionable_test.rb
@@ -25,6 +25,7 @@ class DomainReleasableAuctionableTest < ActiveSupport::TestCase
def test_skips_auction_when_domains_is_blocked
assert_equal 'shop.test', @domain.name
blocked_domains(:one).update!(name: 'shop.test')
+ @domain.save!(validate: false)
@domain.release
@@ -34,6 +35,7 @@ class DomainReleasableAuctionableTest < ActiveSupport::TestCase
def test_skips_auction_when_domains_is_reserved
assert_equal 'shop.test', @domain.name
reserved_domains(:one).update!(name: 'shop.test')
+ @domain.save!(validate: false)
@domain.release
@@ -58,6 +60,24 @@ class DomainReleasableAuctionableTest < ActiveSupport::TestCase
end
end
+ def test_updates_whois_server
+ @domain.update!(delete_date: '2010-07-04')
+ travel_to Time.zone.parse('2010-07-05')
+ old_whois = @domain.whois_record
+
+ Domain.release_domains
+
+ assert_raises ActiveRecord::RecordNotFound do
+ old_whois.reload
+ end
+
+ whois_record = Whois::Record.find_by(name: @domain.name)
+ json = { "name"=>@domain.name,
+ "status"=>["AtAuction"],
+ "disclaimer"=> Setting.registry_whois_disclaimer }
+ assert_equal whois_record.json, json
+ end
+
def test_notifies_registrar
@domain.update!(delete_date: '2010-07-04')
travel_to Time.zone.parse('2010-07-05')
diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb
index ae12f4a1e..15ab8b0c2 100644
--- a/test/models/domain_test.rb
+++ b/test/models/domain_test.rb
@@ -69,6 +69,12 @@ class DomainTest < ActiveSupport::TestCase
domain.name = 'xn--mnchen-3ya.test'
assert domain.valid?
+
+ domain.name = '####'
+ assert domain.invalid?
+
+ domain.name = 'https://example.test'
+ assert domain.invalid?
end
def test_invalid_when_name_is_already_taken
diff --git a/test/models/white_ip_test.rb b/test/models/white_ip_test.rb
index 607887730..ba5abe42f 100644
--- a/test/models/white_ip_test.rb
+++ b/test/models/white_ip_test.rb
@@ -38,6 +38,20 @@ class WhiteIpTest < ActiveSupport::TestCase
assert white_ip.valid?
end
+ def test_validates_include_empty_ipv4
+ white_ip = WhiteIp.new
+
+ white_ip.ipv4 = nil
+ white_ip.ipv6 = '001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ white_ip.registrar = registrars(:bestnames)
+
+ assert_nothing_raised { white_ip.save }
+ assert white_ip.valid?
+
+ assert WhiteIp.include_ip?(white_ip.ipv6)
+ assert_not WhiteIp.include_ip?('192.168.1.1')
+ end
+
private
def valid_white_ip
diff --git a/test/system/admin_area/bank_statement_test.rb b/test/system/admin_area/bank_statement_test.rb
index 6de21b1c3..8630049dc 100644
--- a/test/system/admin_area/bank_statement_test.rb
+++ b/test/system/admin_area/bank_statement_test.rb
@@ -4,6 +4,35 @@ class AdminAreaBankStatementTest < ApplicationSystemTestCase
setup do
sign_in users(:admin)
travel_to Time.zone.parse('2010-07-05 00:30:00')
+
+ @invoice = invoices(:one)
+ end
+
+ def test_update_bank_statement
+ visit admin_bank_statement_path(id: @invoice.id)
+
+ click_link_or_button 'Add'
+
+ fill_in 'Description', with: 'Invoice with id 123'
+ fill_in 'Reference number', with: '1232'
+ fill_in 'Sum', with: '500'
+ fill_in 'Paid at', with: Time.zone.today.to_s
+
+ click_link_or_button 'Save'
+ assert_text 'Bank transaction'
+
+ click_link_or_button 'Edit'
+ fill_in 'Description', with: 'Invoice with id 123'
+ click_link_or_button 'Save'
+
+ assert_text 'Record updated'
+ end
+
+ def test_bind_bank
+ visit admin_bank_statement_path(id: @invoice.id)
+ click_link_or_button 'Bind invoices'
+
+ assert_text 'No invoices were binded'
end
def test_can_create_statement_manually
diff --git a/test/system/admin_area/contacts_test.rb b/test/system/admin_area/contacts_test.rb
index d98882dff..19b15c8a5 100644
--- a/test/system/admin_area/contacts_test.rb
+++ b/test/system/admin_area/contacts_test.rb
@@ -8,6 +8,17 @@ class AdminContactsTest < ApplicationSystemTestCase
sign_in users(:admin)
end
+ def test_update_contact
+ visit admin_contact_path(id: @contact.id)
+ assert_text "#{@contact.name}"
+
+ click_on 'Edit statuses'
+ assert_text "Edit: #{@contact.name}"
+
+ click_on 'Save'
+ assert_text 'Contact updated'
+ end
+
def test_display_list
visit admin_contacts_path
diff --git a/test/system/admin_area/domains/csv_test.rb b/test/system/admin_area/domains/csv_test.rb
new file mode 100644
index 000000000..7d4b44124
--- /dev/null
+++ b/test/system/admin_area/domains/csv_test.rb
@@ -0,0 +1,18 @@
+require 'application_system_test_case'
+
+class AdminAreaCsvTest < ApplicationSystemTestCase
+ setup do
+ sign_in users(:admin)
+ end
+
+ def test_downloads_domain_list_as_csv
+ search_params = {"valid_to_lteq"=>nil}
+ expected_csv = Domain.includes(:registrar, :registrant).search(search_params).result.to_csv
+
+ travel_to Time.zone.parse('2010-07-05 10:30')
+ visit admin_domains_url
+ click_link('CSV')
+ assert_equal "attachment; filename=\"domains.csv\"; filename*=UTF-8''domains.csv", response_headers['Content-Disposition']
+ assert_equal expected_csv, page.body
+ end
+end
diff --git a/test/system/admin_area/domains/legal_doc_test.rb b/test/system/admin_area/domains/legal_doc_test.rb
index 00cc7cc3a..48eedcf4f 100644
--- a/test/system/admin_area/domains/legal_doc_test.rb
+++ b/test/system/admin_area/domains/legal_doc_test.rb
@@ -15,7 +15,7 @@ class AdminAreaDomainsLegalDocTest < ApplicationSystemTestCase
def test_absent_doc_downloading_without_errors
visit admin_domain_url(@domain)
assert_nothing_raised do
- click_on "#{@document.created_at}"
+ click_on "#{@document.created_at}", match: :first
end
end
end
diff --git a/test/system/admin_area/invoices_test.rb b/test/system/admin_area/invoices_test.rb
index 814f95d4a..40a50e1c7 100644
--- a/test/system/admin_area/invoices_test.rb
+++ b/test/system/admin_area/invoices_test.rb
@@ -40,4 +40,4 @@ class AdminAreaInvoicesTest < ApplicationSystemTestCase
assert_current_path admin_invoice_path(@invoice)
assert_text 'Invoice has been sent'
end
-end
\ No newline at end of file
+end
diff --git a/test/system/admin_area/protected_area_test.rb b/test/system/admin_area/protected_area_test.rb
index f3375776a..fa413291b 100644
--- a/test/system/admin_area/protected_area_test.rb
+++ b/test/system/admin_area/protected_area_test.rb
@@ -19,4 +19,4 @@ class AdminAreaProtectedAreaTest < ApplicationSystemTestCase
assert_text 'You are already signed in'
assert_current_path admin_domains_path
end
-end
\ No newline at end of file
+end
diff --git a/test/system/registrar_area/account_activities_test.rb b/test/system/registrar_area/account_activities_test.rb
new file mode 100644
index 000000000..1944dc2fb
--- /dev/null
+++ b/test/system/registrar_area/account_activities_test.rb
@@ -0,0 +1,28 @@
+require 'application_system_test_case'
+
+class RegistrarAccountActivitiesTest < ApplicationSystemTestCase
+ setup do
+ @registrar = registrars(:bestnames)
+ sign_in users(:api_bestnames)
+ end
+
+ def test_show_account_activity_page
+ account_activities(:one).update(sum: "123.00")
+ visit registrar_account_activities_path
+ assert_text 'Account activity'
+ end
+
+ def test_download_account_activity
+ now = Time.zone.parse('2010-07-05 08:00')
+ travel_to now
+ account_activities(:one).update(sum: "123.00")
+
+ get registrar_account_activities_path(format: :csv)
+
+ assert_response :ok
+ assert_equal "text/csv", response.headers['Content-Type']
+ assert_equal %(attachment; filename="account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv"; filename*=UTF-8''account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv),
+ response.headers['Content-Disposition']
+ assert_not_empty response.body
+ end
+end
\ No newline at end of file
diff --git a/test/system/registrar_area/bulk_change/admin_contact_test.rb b/test/system/registrar_area/bulk_change/admin_contact_test.rb
new file mode 100644
index 000000000..8847812cb
--- /dev/null
+++ b/test/system/registrar_area/bulk_change/admin_contact_test.rb
@@ -0,0 +1,49 @@
+require 'application_system_test_case'
+
+class RegistrarAreaAdminContactBulkChangeTest < ApplicationSystemTestCase
+ setup do
+ sign_in users(:api_bestnames)
+ end
+
+ def test_replace_domain_contacts_of_current_registrar
+ request_stub = stub_request(:patch, /domains\/admin_contacts/)
+ .with(body: { current_contact_id: 'william-001', new_contact_id: 'john-001' },
+ basic_auth: ['test_bestnames', 'testtest'])
+ .to_return(body: { data: { affected_domains: %w[foo.test bar.test],
+ skipped_domains: %w[baz.test qux.test] } }.to_json,
+ status: 200)
+
+ visit registrar_domains_url
+ click_link 'Bulk change'
+ click_link 'Admin contact'
+
+ find('.current_admin_contact').fill_in 'Current contact ID', with: 'william-001'
+ find('.new_admin_contact').fill_in 'New contact ID', with: 'john-001'
+ click_on 'Replace admin contacts'
+
+ assert_requested request_stub
+ assert_current_path registrar_domains_path
+ assert_text 'Admin contacts have been successfully replaced'
+ assert_text 'Affected domains: foo.test, bar.test'
+ assert_text 'Skipped domains: baz.test, qux.test'
+ end
+
+ def test_fails_gracefully
+ stub_request(:patch, /domains\/admin_contacts/)
+ .to_return(status: 400,
+ body: { message: 'epic fail' }.to_json,
+ headers: { 'Content-type' => Mime[:json] })
+
+ visit registrar_domains_url
+ click_link 'Bulk change'
+ click_link 'Admin contact'
+
+ find('.current_admin_contact').fill_in 'Current contact ID', with: 'william-001'
+ find('.new_admin_contact').fill_in 'New contact ID', with: 'john-001'
+ click_on 'Replace admin contacts'
+
+ assert_text 'epic fail'
+ assert_field 'Current contact ID', with: 'william-001'
+ assert_field 'New contact ID', with: 'john-001'
+ end
+end
diff --git a/test/system/registrar_area/bulk_change/tech_contact_test.rb b/test/system/registrar_area/bulk_change/tech_contact_test.rb
index e08457f60..055ec25ca 100644
--- a/test/system/registrar_area/bulk_change/tech_contact_test.rb
+++ b/test/system/registrar_area/bulk_change/tech_contact_test.rb
@@ -16,8 +16,8 @@ class RegistrarAreaTechContactBulkChangeTest < ApplicationSystemTestCase
visit registrar_domains_url
click_link 'Bulk change'
- fill_in 'Current contact ID', with: 'william-001'
- fill_in 'New contact ID', with: 'john-001'
+ find('.current_tech_contact').fill_in 'Current contact ID', with: 'william-001'
+ find('.new_tech_contact').fill_in 'New contact ID', with: 'john-001'
click_on 'Replace technical contacts'
assert_requested request_stub
@@ -36,8 +36,8 @@ class RegistrarAreaTechContactBulkChangeTest < ApplicationSystemTestCase
visit registrar_domains_url
click_link 'Bulk change'
- fill_in 'Current contact ID', with: 'william-001'
- fill_in 'New contact ID', with: 'john-001'
+ find('.current_tech_contact').fill_in 'Current contact ID', with: 'william-001'
+ find('.new_tech_contact').fill_in 'New contact ID', with: 'john-001'
click_on 'Replace technical contacts'
assert_text 'epic fail'