Merge branch 'master' into improve-registrant-area

# Conflicts:
#	test/fixtures/contacts.yml
This commit is contained in:
Artur Beljajev 2018-09-01 19:37:44 +03:00
commit 3ca95364ef
51 changed files with 1021 additions and 235 deletions

View file

@ -27,3 +27,4 @@ exclude_patterns:
- "vendor/" - "vendor/"
- "test/" - "test/"
- "spec/" - "spec/"
- "CHANGELOG.md"

View file

@ -0,0 +1,21 @@
module Admin
module Domains
class RegistryLockController < BaseController
def destroy
set_domain
authorize! :manage, @domain
if @domain.remove_registry_lock
redirect_to edit_admin_domain_url(@domain), notice: t('.success')
else
redirect_to edit_admin_domain_url(@domain), alert: t('.error')
end
end
private
def set_domain
@domain = Domain.find(params[:domain_id])
end
end
end
end

View file

@ -1,7 +1,7 @@
module Admin module Admin
class DomainsController < BaseController class DomainsController < BaseController
load_and_authorize_resource before_action :set_domain, only: %i[show edit update keep]
before_action :set_domain, only: [:show, :edit, :update, :zonefile] authorize_resource
helper_method :force_delete_templates helper_method :force_delete_templates
def index def index
@ -33,7 +33,8 @@ module Admin
end end
def show def show
@domain.valid? # Validation is needed to warn users
@domain.validate
end end
def edit def edit
@ -60,6 +61,11 @@ module Admin
@versions = @domain.versions @versions = @domain.versions
end end
def keep
@domain.keep
redirect_to edit_admin_domain_url(@domain), notice: t('.kept')
end
private private
def set_domain def set_domain

View file

@ -47,7 +47,7 @@ module Admin
def destroy def destroy
@mail_template = MailTemplate.find(params[:id]) @mail_template = MailTemplate.find(params[:id])
if @mail_template.destroy if @mail_template.destroy
redirect_to admin_mail_templates_path, notise: t(:deleted) redirect_to admin_mail_templates_path, notice: t(:deleted)
else else
flash.now[:alert] = I18n.t(:failure) flash.now[:alert] = I18n.t(:failure)
render 'show' render 'show'

View file

@ -6,6 +6,7 @@ module Api
module Registrant module Registrant
class BaseController < ActionController::API class BaseController < ActionController::API
before_action :authenticate before_action :authenticate
before_action :set_paper_trail_whodunnit
rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception| rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
error = {} error = {}
@ -22,16 +23,32 @@ module Api
header.gsub(pattern, '') if header&.match(pattern) header.gsub(pattern, '') if header&.match(pattern)
end end
def associated_domains(user)
country_code, ident = user.registrant_ident.split('-')
BusinessRegistryCache.fetch_associated_domains(ident, country_code)
rescue Soap::Arireg::NotAvailableError => error
Rails.logger.fatal("[EXCEPTION] #{error}")
user.domains
end
def authenticate def authenticate
decryptor = AuthTokenDecryptor.create_with_defaults(bearer_token) decryptor = AuthTokenDecryptor.create_with_defaults(bearer_token)
decryptor.decrypt_token decryptor.decrypt_token
if decryptor.valid? if decryptor.valid?
sign_in decryptor.user sign_in(:registrant_user, decryptor.user)
else else
render json: { errors: [{base: ['Not authorized']}] }, status: :unauthorized render json: { errors: [{ base: ['Not authorized'] }] },
status: :unauthorized
end end
end end
# This controller does not inherit from ApplicationController,
# so user_for_paper_trail method is not usable.
def set_paper_trail_whodunnit
::PaperTrail.whodunnit = current_registrant_user.id_role_username
end
end end
end end
end end

View file

@ -30,17 +30,6 @@ module Api
render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found
end end
end end
private
def associated_domains(user)
country_code, ident = user.registrant_ident.split('-')
BusinessRegistryCache.fetch_associated_domains(ident, country_code)
rescue Soap::Arireg::NotAvailableError => error
Rails.logger.fatal("[EXCEPTION] #{error}")
user.domains
end
end end
end end
end end

View file

@ -0,0 +1,48 @@
module Api
module V1
module Registrant
class RegistryLocksController < BaseController
before_action :set_domain
before_action :authorized_to_manage_locks?
def create
if @domain.apply_registry_lock
render json: @domain
else
render json: { errors: [{ base: ['Domain cannot be locked'] }] },
status: :unprocessable_entity
end
end
def destroy
if @domain.remove_registry_lock
render json: @domain
else
render json: { errors: [{ base: ['Domain is not locked'] }] },
status: :unprocessable_entity
end
end
private
def set_domain
domain_pool = current_registrant_user.domains
@domain = domain_pool.find_by(uuid: params[:domain_uuid])
return if @domain
render json: { errors: [{ base: ['Domain not found'] }] },
status: :not_found and return
end
def authorized_to_manage_locks?
return if current_registrant_user.administered_domains.include?(@domain)
render json: { errors: [
{ base: ['Only administrative contacts can manage registry locks'] }
] },
status: :unauthorized and return
end
end
end
end
end

View file

@ -1,16 +1,25 @@
module Concerns::Domain::Deletable module Concerns::Domain::Deletable
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do private
alias_attribute :delete_time, :delete_at
def delete_later
deletion_time = Time.zone.at(rand(deletion_time_span))
DomainDeleteJob.enqueue(id, run_at: deletion_time, priority: 1)
logger.info "Domain #{name} is scheduled to be deleted around #{deletion_time}"
end end
def discard def do_not_delete_later
statuses << DomainStatus::DELETE_CANDIDATE # Que job can be manually deleted in admin area UI
save QueJob.find_by("args->>0 = '#{id}'", job_class: DomainDeleteJob.name)&.destroy
end end
def discarded? def deletion_time_span
statuses.include?(DomainStatus::DELETE_CANDIDATE) range_params = [Time.zone.now.to_i, deletion_deadline.to_i].sort
Range.new(*range_params)
end end
end
def deletion_deadline
delete_at + 24.hours
end
end

View file

@ -0,0 +1,40 @@
module Concerns::Domain::Discardable
extend ActiveSupport::Concern
class_methods do
def discard_domains
domains = where('delete_at < ? AND ? != ALL(coalesce(statuses, array[]::varchar[])) AND' \
' ? != ALL(COALESCE(statuses, array[]::varchar[]))',
Time.zone.now,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::DELETE_CANDIDATE)
domains.each do |domain|
domain.discard
yield domain if block_given?
end
end
end
def discard
raise 'Domain is already discarded' if discarded?
statuses << DomainStatus::DELETE_CANDIDATE
transaction do
save(validate: false)
delete_later
end
end
def keep
statuses.delete(DomainStatus::DELETE_CANDIDATE)
transaction do
save(validate: false)
do_not_delete_later
end
end
def discarded?
statuses.include?(DomainStatus::DELETE_CANDIDATE)
end
end

View file

@ -0,0 +1,51 @@
module Concerns
module Domain
module RegistryLockable
extend ActiveSupport::Concern
def apply_registry_lock
return unless registry_lockable?
return if locked_by_registrant?
transaction do
statuses << DomainStatus::SERVER_UPDATE_PROHIBITED
statuses << DomainStatus::SERVER_DELETE_PROHIBITED
statuses << DomainStatus::SERVER_TRANSFER_PROHIBITED
self.locked_by_registrant_at = Time.zone.now
save!
end
end
def registry_lockable?
(statuses & [DomainStatus::PENDING_DELETE_CONFIRMATION,
DomainStatus::PENDING_CREATE, DomainStatus::PENDING_UPDATE,
DomainStatus::PENDING_DELETE, DomainStatus::PENDING_RENEW,
DomainStatus::PENDING_TRANSFER, DomainStatus::FORCE_DELETE]).empty?
end
def locked_by_registrant?
return false unless locked_by_registrant_at
lock_statuses = [DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::SERVER_TRANSFER_PROHIBITED]
(statuses & lock_statuses).count == 3
end
def remove_registry_lock
return unless locked_by_registrant?
transaction do
statuses.delete(DomainStatus::SERVER_UPDATE_PROHIBITED)
statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED)
statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED)
self.locked_by_registrant_at = nil
save!
end
end
end
end
end

View file

@ -37,7 +37,7 @@ module Versions
registrar = Registrar.find_by(name: str) registrar = Registrar.find_by(name: str)
user = registrar.api_users.first if registrar user = registrar.api_users.first if registrar
str_match = str.match(/^(\d+)-(ApiUser:|api-|AdminUser:)/) str_match = str.match(/^(\d+)-(ApiUser:|api-|AdminUser:|RegistrantUser:)/)
user ||= User.find_by(id: str_match[1]) if str_match user ||= User.find_by(id: str_match[1]) if str_match
user user

View file

@ -4,8 +4,10 @@ class Domain < ActiveRecord::Base
include Concerns::Domain::Expirable include Concerns::Domain::Expirable
include Concerns::Domain::Activatable include Concerns::Domain::Activatable
include Concerns::Domain::ForceDelete include Concerns::Domain::ForceDelete
include Concerns::Domain::Discardable
include Concerns::Domain::Deletable include Concerns::Domain::Deletable
include Concerns::Domain::Transferable include Concerns::Domain::Transferable
include Concerns::Domain::RegistryLockable
has_paper_trail class_name: "DomainVersion", meta: { children: :children_log } has_paper_trail class_name: "DomainVersion", meta: { children: :children_log }
@ -249,13 +251,6 @@ class Domain < ActiveRecord::Base
true true
end end
def delete_candidateable?
return false if delete_at > Time.zone.now
return false if statuses.include?(DomainStatus::DELETE_CANDIDATE)
return false if statuses.include?(DomainStatus::SERVER_DELETE_PROHIBITED)
true
end
def renewable? def renewable?
if Setting.days_to_renew_domain_before_expire != 0 if Setting.days_to_renew_domain_before_expire != 0
# if you can renew domain at days_to_renew before domain expiration # if you can renew domain at days_to_renew before domain expiration
@ -613,10 +608,6 @@ class Domain < ActiveRecord::Base
where("#{attribute_alias(:outzone_time)} < ?", Time.zone.now) where("#{attribute_alias(:outzone_time)} < ?", Time.zone.now)
end end
def self.delete_candidates
where("#{attribute_alias(:delete_time)} < ?", Time.zone.now)
end
def self.uses_zone?(zone) def self.uses_zone?(zone)
exists?(["name ILIKE ?", "%.#{zone.origin}"]) exists?(["name ILIKE ?", "%.#{zone.origin}"])
end end

View file

@ -84,22 +84,6 @@ class DomainCron
c = 0 c = 0
domains = Domain.delete_candidates
domains.each do |domain|
next unless domain.delete_candidateable?
domain.statuses << DomainStatus::DELETE_CANDIDATE
# If domain successfully saved, add it to delete schedule
if domain.save(validate: false)
::PaperTrail.whodunnit = "cron - #{__method__}"
DomainDeleteJob.enqueue(domain.id, run_at: rand(((24*60) - (DateTime.now.hour * 60 + DateTime.now.minute))).minutes.from_now)
STDOUT << "#{Time.zone.now.utc} DomainCron.destroy_delete_candidates: job added by deleteCandidate status ##{domain.id} (#{domain.name})\n" unless Rails.env.test?
c += 1
end
end
Domain.where('force_delete_at <= ?', Time.zone.now.end_of_day.utc).each do |x| Domain.where('force_delete_at <= ?', Time.zone.now.end_of_day.utc).each do |x|
DomainDeleteJob.enqueue(x.id, run_at: rand(((24*60) - (DateTime.now.hour * 60 + DateTime.now.minute))).minutes.from_now) DomainDeleteJob.enqueue(x.id, run_at: rand(((24*60) - (DateTime.now.hour * 60 + DateTime.now.minute))).minutes.from_now)
STDOUT << "#{Time.zone.now.utc} DomainCron.destroy_delete_candidates: job added by force delete time ##{x.id} (#{x.name})\n" unless Rails.env.test? STDOUT << "#{Time.zone.now.utc} DomainCron.destroy_delete_candidates: job added by force delete time ##{x.id} (#{x.name})\n" unless Rails.env.test?

View file

@ -153,13 +153,11 @@ class DomainStatus < ActiveRecord::Base
[ [
['Hold', SERVER_HOLD], ['Hold', SERVER_HOLD],
['ManualInzone', SERVER_MANUAL_INZONE], ['ManualInzone', SERVER_MANUAL_INZONE],
# [''],
['RenewProhibited', SERVER_RENEW_PROHIBITED], ['RenewProhibited', SERVER_RENEW_PROHIBITED],
['TransferProhibited', SERVER_TRANSFER_PROHIBITED], ['TransferProhibited', SERVER_TRANSFER_PROHIBITED],
['RegistrantChangeProhibited', SERVER_REGISTRANT_CHANGE_PROHIBITED], ['RegistrantChangeProhibited', SERVER_REGISTRANT_CHANGE_PROHIBITED],
['AdminChangeProhibited', SERVER_ADMIN_CHANGE_PROHIBITED], ['AdminChangeProhibited', SERVER_ADMIN_CHANGE_PROHIBITED],
['TechChangeProhibited', SERVER_TECH_CHANGE_PROHIBITED], ['TechChangeProhibited', SERVER_TECH_CHANGE_PROHIBITED],
# [''],
['UpdateProhibited', SERVER_UPDATE_PROHIBITED], ['UpdateProhibited', SERVER_UPDATE_PROHIBITED],
['DeleteProhibited', SERVER_DELETE_PROHIBITED] ['DeleteProhibited', SERVER_DELETE_PROHIBITED]
] ]
@ -171,11 +169,11 @@ class DomainStatus < ActiveRecord::Base
INACTIVE, INACTIVE,
FORCE_DELETE, FORCE_DELETE,
PENDING_CREATE, PENDING_CREATE,
#PENDING_DELETE,
PENDING_RENEW, PENDING_RENEW,
PENDING_TRANSFER, PENDING_TRANSFER,
PENDING_UPDATE, PENDING_UPDATE,
PENDING_DELETE_CONFIRMATION PENDING_DELETE_CONFIRMATION,
DELETE_CANDIDATE,
] ]
end end
end end

4
app/models/que_job.rb Normal file
View file

@ -0,0 +1,4 @@
# To be able to remove existing jobs
class QueJob < ActiveRecord::Base
self.primary_key = 'job_id'
end

View file

@ -1,5 +1,5 @@
class RegistrantUser < User class RegistrantUser < User
ACCEPTED_ISSUER = 'AS Sertifitseerimiskeskus' ACCEPTED_ISSUER = 'AS Sertifitseerimiskeskus'.freeze
attr_accessor :idc_data attr_accessor :idc_data
devise :database_authenticatable, :trackable, :timeoutable devise :database_authenticatable, :trackable, :timeoutable
@ -10,16 +10,46 @@ class RegistrantUser < User
delegate :can?, :cannot?, to: :ability delegate :can?, :cannot?, to: :ability
def ident def ident
registrant_ident.to_s.split("-").last registrant_ident.to_s.split('-').last
end end
def country_code
registrant_ident.to_s.split('-').first
end
# In Rails 5, can be replaced with a much simpler `or` query method and the raw SQL parts can be
# removed.
# https://guides.rubyonrails.org/active_record_querying.html#or-conditions
def domains def domains
ident_cc, ident = registrant_ident.to_s.split '-' domains_where_is_contact = begin
Domain.includes(:registrar, :registrant).where(contacts: { Domain.joins(:domain_contacts)
ident_type: 'priv', .where(domain_contacts: { contact_id: contacts })
ident: ident, #identity_code, end
ident_country_code: ident_cc #country_code
}) domains_where_is_registrant = Domain.where(registrant_id: contacts)
Domain.from(
"(#{domains_where_is_registrant.to_sql} UNION " \
"#{domains_where_is_contact.to_sql}) AS domains"
)
end
def contacts
Contact.where(ident_type: 'priv', ident: ident, ident_country_code: country_code)
end
def administered_domains
domains_where_is_administrative_contact = begin
Domain.joins(:domain_contacts)
.where(domain_contacts: { contact_id: contacts, type: [AdminDomainContact] })
end
domains_where_is_registrant = Domain.where(registrant_id: contacts)
Domain.from(
"(#{domains_where_is_registrant.to_sql} UNION " \
"#{domains_where_is_administrative_contact.to_sql}) AS domains"
)
end end
def to_s def to_s
@ -35,13 +65,13 @@ class RegistrantUser < User
user_data = {} user_data = {}
# handling here new and old mode # handling here new and old mode
if idc_data.starts_with?("/") if idc_data.starts_with?('/')
user_data[:ident] = idc_data.scan(/serialNumber=(\d+)/).flatten.first user_data[:ident] = idc_data.scan(/serialNumber=(\d+)/).flatten.first
user_data[:country_code] = idc_data.scan(/^\/C=(.{2})/).flatten.first user_data[:country_code] = idc_data.scan(/^\/C=(.{2})/).flatten.first
user_data[:first_name] = idc_data.scan(%r{/GN=(.+)/serialNumber}).flatten.first user_data[:first_name] = idc_data.scan(%r{/GN=(.+)/serialNumber}).flatten.first
user_data[:last_name] = idc_data.scan(%r{/SN=(.+)/GN}).flatten.first user_data[:last_name] = idc_data.scan(%r{/SN=(.+)/GN}).flatten.first
else else
parse_str = "," + idc_data parse_str = ',' + idc_data
user_data[:ident] = parse_str.scan(/,serialNumber=(\d+)/).flatten.first user_data[:ident] = parse_str.scan(/,serialNumber=(\d+)/).flatten.first
user_data[:country_code] = parse_str.scan(/,C=(.{2})/).flatten.first user_data[:country_code] = parse_str.scan(/,C=(.{2})/).flatten.first
user_data[:first_name] = parse_str.scan(/,GN=([^,]+)/).flatten.first user_data[:first_name] = parse_str.scan(/,GN=([^,]+)/).flatten.first

View file

@ -14,6 +14,11 @@ class DomainPresenter
html += " #{label}" html += " #{label}"
end end
if domain.locked_by_registrant?
label = view.content_tag(:span, 'registryLock', class: 'label label-danger')
html += " #{label}"
end
html.html_safe html.html_safe
end end
@ -30,7 +35,7 @@ class DomainPresenter
end end
def delete_date def delete_date
view.l(domain.delete_time, format: :date) if domain.delete_time view.l(domain.delete_at, format: :date) if domain.delete_at
end end
def force_delete_date def force_delete_date
@ -59,11 +64,30 @@ class DomainPresenter
end end
end end
def remove_registry_lock_btn
return unless domain.locked_by_registrant?
view.link_to(view.t('admin.domains.registry_lock.destroy.btn'),
view.admin_domain_registry_lock_path(domain),
method: :delete,
data: { confirm: view.t('admin.domains.registry_lock.destroy.confirm') },
class: 'dropdown-item')
end
def keep_btn
return unless domain.discarded?
view.link_to view.t('admin.domains.edit.keep_btn'), view.keep_admin_domain_path(@domain),
method: :patch,
data: { confirm: view.t('admin.domains.edit.keep_btn_confirm') },
class: 'dropdown-item'
end
private private
def schedule_force_delete_btn def schedule_force_delete_btn
view.content_tag(:a, view.t('admin.domains.force_delete_toggle_btn.schedule'), view.content_tag(:a, view.t('admin.domains.force_delete_toggle_btn.schedule'),
class: 'btn btn-default', class: 'dropdown-item',
data: { data: {
toggle: 'modal', toggle: 'modal',
target: '.domain-edit-force-delete-dialog', target: '.domain-edit-force-delete-dialog',
@ -77,14 +101,14 @@ class DomainPresenter
data: { data: {
confirm: view.t('admin.domains.force_delete_toggle_btn.cancel_confirm'), confirm: view.t('admin.domains.force_delete_toggle_btn.cancel_confirm'),
}, },
class: 'btn btn-primary' class: 'dropdown-item'
end end
def inactive_schedule_force_delete_btn def inactive_schedule_force_delete_btn
view.content_tag :button, view.t('admin.domains.force_delete_toggle_btn.schedule'), view.content_tag :button, view.t('admin.domains.force_delete_toggle_btn.schedule'),
title: view.t('admin.domains.force_delete_toggle_btn.unable_to_schedule'), title: view.t('admin.domains.force_delete_toggle_btn.unable_to_schedule'),
disabled: true, disabled: true,
class: 'btn btn-default' class: 'dropdown-item'
end end
attr_reader :domain attr_reader :domain

View file

@ -5,18 +5,31 @@
<li><%= link_to @domain, admin_domain_path(@domain) %></li> <li><%= link_to @domain, admin_domain_path(@domain) %></li>
</ol> </ol>
<div class="page-header"> <div class="page-header">
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-8">
<h1><%= t '.header', domain: domain.name %></h1> <h1 class="text-center-xs">
<%= t '.header' %> <%= domain.name_with_status %>
</h1>
</div> </div>
<div class="col-sm-4 text-right">
<div class="col-sm-8 text-right"> <div class="btn-group">
<%= link_to t('.add_new_status_btn'), '#', class: 'btn btn-primary js-add-status' %> <button class="btn btn-primary" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= domain.force_delete_toggle_btn %> Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><%= domain.force_delete_toggle_btn %></li>
<li><%= domain.remove_registry_lock_btn %></li>
<li><%= domain.keep_btn %></li>
<div class="divider"></div>
<li><%= link_to t('.add_new_status_btn'), '#', class: 'js-add-status' %></li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
<%= render 'form' %> <%= render 'form' %>
<%= render 'force_delete_dialog', domain: @domain, templates: force_delete_templates %> <%= render 'force_delete_dialog', domain: @domain, templates: force_delete_templates %>

View file

@ -33,6 +33,9 @@
<dt><%= Domain.human_attribute_name :force_delete_at %></dt> <dt><%= Domain.human_attribute_name :force_delete_at %></dt>
<dd><%= l @domain.force_delete_at %></dd> <dd><%= l @domain.force_delete_at %></dd>
<dt><%= t('.locked_by_registrant_at') %></dt>
<dd><%= l(@domain.locked_by_registrant_at) %></dd>
</dl> </dl>
</div> </div>
</div> </div>

View file

@ -13,8 +13,13 @@ en:
reset_btn: Reset reset_btn: Reset
edit: edit:
header: "Edit: %{domain}" header: "Edit:"
add_new_status_btn: Add new status add_new_status_btn: Add new status
keep_btn: Remove deleteCandidate status
keep_btn_confirm: Are you sure you want to remove deleteCandidate status?
keep:
kept: deleteCandidate status has been removed
force_delete_dialog: force_delete_dialog:
title: Force delete title: Force delete
@ -23,6 +28,13 @@ en:
close_btn: Close dialog close_btn: Close dialog
submit_btn: Force delete domain submit_btn: Force delete domain
registry_lock:
destroy:
btn: Remove registry lock
confirm: Are you sure you want to remove the registry lock?
success: Registry lock removed
error: Registry lock could not be removed
versions: versions:
time: Time time: Time
registrant: Registrant registrant: Registrant
@ -33,6 +45,8 @@ en:
general: general:
outzone_time: Outzone time outzone_time: Outzone time
delete_time: Delete time delete_time: Delete time
force_delete_time: Force delete time
locked_by_registrant_at: Registry lock time
admin_contacts: admin_contacts:
title: Admin. contacts title: Admin. contacts
@ -48,4 +62,4 @@ en:
unable_to_schedule: > unable_to_schedule: >
Force delete procedure cannot be scheduled while a domain has deleteCandidate status Force delete procedure cannot be scheduled while a domain has deleteCandidate status
cancel: Cancel force delete cancel: Cancel force delete
cancel_confirm: Are you sure you want cancel force delete procedure? cancel_confirm: Are you sure you want cancel force delete procedure?

View file

@ -23,7 +23,9 @@ Rails.application.routes.draw do
namespace :registrant do namespace :registrant do
post 'auth/eid', to: 'auth#eid' post 'auth/eid', to: 'auth#eid'
resources :domains, only: %i[index show], param: :uuid resources :domains, only: %i[index show], param: :uuid do
resource :registry_lock, only: %i[create destroy]
end
resources :contacts, only: %i[index show], param: :uuid resources :contacts, only: %i[index show], param: :uuid
end end
end end
@ -191,11 +193,16 @@ Rails.application.routes.draw do
match 'forward', via: [:post, :get] match 'forward', via: [:post, :get]
end end
resources :domains do resources :domains, except: %i[new create destroy] do
resources :domain_versions, controller: 'domains', action: 'versions' resources :domain_versions, controller: 'domains', action: 'versions'
resources :pending_updates resources :pending_updates
resources :pending_deletes resources :pending_deletes
resource :force_delete, controller: 'domains/force_delete', only: %i[create destroy] resource :force_delete, controller: 'domains/force_delete', only: %i[create destroy]
resource :registry_lock, controller: 'domains/registry_lock', only: :destroy
member do
patch :keep
end
end end
resources :domain_versions do resources :domain_versions do
@ -262,4 +269,4 @@ Rails.application.routes.draw do
# To prevent users seeing the default welcome message "Welcome aboard" from Rails # To prevent users seeing the default welcome message "Welcome aboard" from Rails
root to: redirect('admin/sign_in') root to: redirect('admin/sign_in')
end end

View file

@ -57,6 +57,10 @@ if @cron_group == 'registry'
every :day, at: '19:00pm' do every :day, at: '19:00pm' do
runner 'Directo.send_receipts' runner 'Directo.send_receipts'
end if @environment == 'production' end if @environment == 'production'
every 42.minutes do
rake 'domain:discard'
end
end end
every 10.minutes do every 10.minutes do

View file

@ -0,0 +1,7 @@
class AddRegistryLockTimeColumn < ActiveRecord::Migration
def change
change_table(:domains) do |t|
t.column :locked_by_registrant_at, :datetime, null: true
end
end
end

View file

@ -0,0 +1,5 @@
class ChangeContactsEmailToNotNull < ActiveRecord::Migration
def change
change_column_null :contacts, :email, false
end
end

View file

@ -0,0 +1,9 @@
class ChangeDomainPendingJsonToJsonb < ActiveRecord::Migration
def up
change_column :domains, :pending_json, 'jsonb USING CAST(pending_json AS jsonb)'
end
def down
change_column :domains, :pending_json, 'json USING CAST(pending_json AS json)'
end
end

View file

@ -613,7 +613,7 @@ CREATE TABLE public.contacts (
id integer NOT NULL, id integer NOT NULL,
code character varying NOT NULL, code character varying NOT NULL,
phone character varying, phone character varying,
email character varying, email character varying NOT NULL,
fax character varying, fax character varying,
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone, updated_at timestamp without time zone,
@ -904,7 +904,7 @@ CREATE TABLE public.domains (
delete_at timestamp without time zone, delete_at timestamp without time zone,
registrant_verification_asked_at timestamp without time zone, registrant_verification_asked_at timestamp without time zone,
registrant_verification_token character varying, registrant_verification_token character varying,
pending_json json, pending_json jsonb,
force_delete_at timestamp without time zone, force_delete_at timestamp without time zone,
statuses character varying[], statuses character varying[],
reserved boolean DEFAULT false, reserved boolean DEFAULT false,
@ -912,7 +912,8 @@ CREATE TABLE public.domains (
statuses_before_force_delete character varying[] DEFAULT '{}'::character varying[], statuses_before_force_delete character varying[] DEFAULT '{}'::character varying[],
upid integer, upid integer,
up_date timestamp without time zone, up_date timestamp without time zone,
uuid uuid DEFAULT public.gen_random_uuid() NOT NULL uuid uuid DEFAULT public.gen_random_uuid() NOT NULL,
locked_by_registrant_at timestamp without time zone
); );
@ -4761,3 +4762,9 @@ INSERT INTO schema_migrations (version) VALUES ('20180613045614');
INSERT INTO schema_migrations (version) VALUES ('20180713154915'); INSERT INTO schema_migrations (version) VALUES ('20180713154915');
INSERT INTO schema_migrations (version) VALUES ('20180808064402');
INSERT INTO schema_migrations (version) VALUES ('20180816123540');
INSERT INTO schema_migrations (version) VALUES ('20180824092855');

View file

@ -7,5 +7,5 @@ Main communication specification through Registrant API:
[Authentication](registrant-api/v1/authentication.md) [Authentication](registrant-api/v1/authentication.md)
[Domains](registrant-api/v1/domain.md) [Domains](registrant-api/v1/domain.md)
[Domain Lock](registrant-api/v1/domain_lock.md) [Registry Lock](registrant-api/v1/registry_lock.md)
[Contacts](registrant-api/v1/contact.md) [Contacts](registrant-api/v1/contact.md)

View file

@ -1,4 +1,4 @@
# Domain locks # Registry lock
## POST api/v1/registrant/domains/$UUID/registry_lock ## POST api/v1/registrant/domains/$UUID/registry_lock
@ -59,12 +59,12 @@ Content-Type: application/json
#### Response for failure #### Response for failure
``` ```
HTTP/1.1 400 HTTP/1.1 422
Content-Type: application/json Content-Type: application/json
{ {
"errors": [ "errors": [
{ "base": "domain cannot be locked" } { "base": "Domain cannot be locked" }
] ]
} }
@ -76,11 +76,23 @@ Content-Type: application/json
{ {
"errors": [ "errors": [
{ "base": "domain does not exist" } { "base": "Domain not found" }
] ]
} }
``` ```
```
HTTP/1.1 401
Content-Type: application/json
{
"errors": [
{ "base": ["Only administrative contacts can manage registry locks"] }
]
}
```
## DELETE api/v1/registrant/domains/$UUID/registry_lock ## DELETE api/v1/registrant/domains/$UUID/registry_lock
@ -139,12 +151,12 @@ Content-Type: application/json
#### Response for failure #### Response for failure
``` ```
HTTP/1.1 400 HTTP/1.1 422
Content-Type: application/json Content-Type: application/json
{ {
"errors": [ "errors": [
{ "base": "domain cannot be unlocked" } { "base": "Domain is not locked" }
] ]
} }
@ -156,7 +168,19 @@ Content-Type: application/json
{ {
"errors": [ "errors": [
{ "base": "domain does not exist" } { "base": "Domain not found" }
]
}
```
```
HTTP/1.1 401
Content-Type: application/json
{
"errors": [
{ "base": ["Only administrative contacts can manage registry locks"] }
] ]
} }

13
lib/tasks/domain.rake Normal file
View file

@ -0,0 +1,13 @@
namespace :domain do
desc 'Discard domains'
task discard: :environment do
domain_count = 0
Domain.discard_domains do |domain|
puts "#{domain} is discarded"
domain_count = domain_count + 1
end
puts "Discarded total: #{domain_count}"
end
end

View file

@ -298,50 +298,6 @@ RSpec.describe Domain do
@domain.registrant_update_confirmable?('123').should == false @domain.registrant_update_confirmable?('123').should == false
end end
end end
context 'with versioning' do
it 'should not have one version' do
with_versioning do
@domain.versions.size.should == 0
@domain.name = 'new-test-name.ee'
@domain.save
@domain.errors.full_messages.should match_array([])
@domain.versions.size.should == 1
end
end
it 'should return api_creator when created by api user' do
with_versioning do
@user = create(:admin_user)
@api_user = create(:api_user)
@user.id.should == 1
@api_user.id.should == 2
::PaperTrail.whodunnit = '2-ApiUser: testuser'
@domain = create(:domain)
@domain.creator_str.should == '2-ApiUser: testuser'
@domain.creator.should == @api_user
@domain.creator.should_not == @user
end
end
it 'should return api_creator when created by api user' do
with_versioning do
@user = create(:admin_user, id: 1000)
@api_user = create(:api_user, id: 2000)
@user.id.should == 1000
@api_user.id.should == 2000
::PaperTrail.whodunnit = '1000-AdminUser: testuser'
@domain = create(:domain)
@domain.creator_str.should == '1000-AdminUser: testuser'
@domain.creator.should == @user
@domain.creator.should_not == @api_user
end
end
end
end end
it 'validates domain name' do it 'validates domain name' do
@ -839,22 +795,6 @@ RSpec.describe Domain do
end end
end end
describe '::delete_candidates', db: true do
before :example do
travel_to Time.zone.parse('05.07.2010 00:00')
create(:zone, origin: 'ee')
create(:domain, id: 1, delete_time: Time.zone.parse('04.07.2010 23:59'))
create(:domain, id: 2, delete_time: Time.zone.parse('05.07.2010 00:00'))
create(:domain, id: 3, delete_time: Time.zone.parse('05.07.2010 00:01'))
end
it 'returns domains with delete time in the past' do
expect(described_class.delete_candidates.ids).to eq([1])
end
end
describe '::uses_zone?', db: true do describe '::uses_zone?', db: true do
let!(:zone) { create(:zone, origin: 'domain.tld') } let!(:zone) { create(:zone, origin: 'domain.tld') }

View file

@ -44,7 +44,7 @@ RSpec.describe DomainPresenter do
subject(:delete_date) { presenter.delete_date } subject(:delete_date) { presenter.delete_date }
context 'when present' do context 'when present' do
let(:domain) { instance_double(Domain, delete_time: '05.07.2010') } let(:domain) { instance_double(Domain, delete_at: '05.07.2010') }
it 'returns localized date' do it 'returns localized date' do
expect(view).to receive(:l).with('05.07.2010', format: :date).and_return('delete date') expect(view).to receive(:l).with('05.07.2010', format: :date).and_return('delete date')
@ -53,7 +53,7 @@ RSpec.describe DomainPresenter do
end end
context 'when absent' do context 'when absent' do
let(:domain) { instance_double(Domain, delete_time: nil) } let(:domain) { instance_double(Domain, delete_at: nil) }
specify { expect(delete_date).to be_nil } specify { expect(delete_date).to be_nil }
end end

View file

@ -17,7 +17,7 @@ william: &william
email: william@inbox.test email: william@inbox.test
phone: '+555.555' phone: '+555.555'
fax: '+666.6' fax: '+666.6'
ident: 1234 ident: 12345
ident_type: priv ident_type: priv
ident_country_code: US ident_country_code: US
registrar: bestnames registrar: bestnames
@ -36,7 +36,7 @@ jane:
name: Jane name: Jane
email: jane@mail.test email: jane@mail.test
phone: '+555.555' phone: '+555.555'
ident: 1234 ident: 123456
ident_type: priv ident_type: priv
ident_country_code: US ident_country_code: US
registrar: bestnames registrar: bestnames
@ -48,7 +48,7 @@ acme_ltd:
name: Acme Ltd name: Acme Ltd
email: acme@outlook.test email: acme@outlook.test
phone: '+555.555' phone: '+555.555'
ident: 1234 ident: 1234567
ident_type: org ident_type: org
registrar: bestnames registrar: bestnames
ident_country_code: US ident_country_code: US
@ -60,7 +60,7 @@ jack:
name: Jack name: Jack
email: jack@inbox.test email: jack@inbox.test
phone: '+555.555' phone: '+555.555'
ident: 12345 ident: 12345678
ident_type: org ident_type: org
registrar: goodnames registrar: goodnames
ident_country_code: US ident_country_code: US
@ -89,4 +89,4 @@ invalid:
email: invalid@invalid.test email: invalid@invalid.test
auth_info: any auth_info: any
registrar: bestnames registrar: bestnames
uuid: bd80c0f9-26ee-49e0-a2cb-2311d931c433 uuid: bd80c0f9-26ee-49e0-a2cb-2311d931c433

View file

@ -28,11 +28,36 @@ airport_william_tech:
contact: william contact: william
type: TechDomainContact type: TechDomainContact
library_john: library_acme_admin:
domain: library
contact: acme_ltd
type: AdminDomainContact
library_john_tech:
domain: library domain: library
contact: john contact: john
type: TechDomainContact
metro_jack_admin:
domain: metro
contact: jack
type: AdminDomainContact type: AdminDomainContact
metro_jack_tech:
domain: metro
contact: jack
type: TechDomainContact
hospital_john_admin:
domain: hospital
contact: john
type: AdminDomainContact
hospital_john_tech:
domain: hospital
contact: john
type: TechDomainContact
invalid_invalid_admin: invalid_invalid_admin:
domain: invalid domain: invalid
contact: invalid contact: invalid

View file

@ -0,0 +1,131 @@
require 'test_helper'
require 'auth_token/auth_token_creator'
class RegistrantApiRegistryLocksTest < ApplicationIntegrationTest
def setup
super
@original_registry_time = Setting.days_to_keep_business_registry_cache
Setting.days_to_keep_business_registry_cache = 1
travel_to Time.zone.parse('2010-07-05')
@user = users(:registrant)
@domain = domains(:airport)
@auth_headers = { 'HTTP_AUTHORIZATION' => auth_token }
end
def teardown
super
Setting.days_to_keep_business_registry_cache = @original_registry_time
travel_back
end
def test_can_lock_a_not_locked_domain
post '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert(response_json[:statuses].include?(DomainStatus::SERVER_DELETE_PROHIBITED))
assert(response_json[:statuses].include?(DomainStatus::SERVER_TRANSFER_PROHIBITED))
assert(response_json[:statuses].include?(DomainStatus::SERVER_UPDATE_PROHIBITED))
@domain.reload
assert(@domain.locked_by_registrant?)
end
def test_locking_a_domain_creates_a_version_record
assert_difference '@domain.versions.count', 1 do
post '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
end
@domain.reload
assert_equal(@domain.updator, @user)
end
def test_cannot_lock_a_domain_in_pending_state
@domain.statuses << DomainStatus::PENDING_UPDATE
@domain.save
post '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal(422, response.status)
assert_equal({ errors: [{ base: ['Domain cannot be locked'] }] }, response_json)
end
def test_cannot_lock_an_already_locked_domain
@domain.apply_registry_lock
assert(@domain.locked_by_registrant?)
post '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal(422, response.status)
assert_equal({ errors: [{ base: ['Domain cannot be locked'] }] }, response_json)
end
def test_can_unlock_a_locked_domain
@domain.apply_registry_lock
delete '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert(response_json[:statuses].include?(DomainStatus::OK))
@domain.reload
refute(@domain.locked_by_registrant?)
end
def test_cannot_unlock_a_not_locked_domain
delete '/api/v1/registrant/domains/2df2c1a1-8f6a-490a-81be-8bdf29866880/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal(422, response.status)
assert_equal({ errors: [{ base: ['Domain is not locked'] }] }, response_json)
end
def test_returns_404_when_domain_is_not_found
post '/api/v1/registrant/domains/random-uuid/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal(404, response.status)
assert_equal({ errors: [{ base: ['Domain not found'] }] }, response_json)
end
def test_technical_contact_cannot_lock_a_domain
post '/api/v1/registrant/domains/647bcc48-8d5e-4a04-8ce5-2a3cd17b6eab/registry_lock',
{}, @auth_headers
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal(401, response.status)
assert_equal({ errors: [{ base: ['Only administrative contacts can manage registry locks'] }] },
response_json)
end
def test_registrant_can_lock_a_domain
post '/api/v1/registrant/domains/1b3ee442-e8fe-4922-9492-8fcb9dccc69c/registry_lock',
{}, @auth_headers
assert_equal(200, response.status)
response_json = JSON.parse(response.body, symbolize_names: true)
assert(response_json[:statuses].include?(DomainStatus::SERVER_DELETE_PROHIBITED))
assert(response_json[:statuses].include?(DomainStatus::SERVER_TRANSFER_PROHIBITED))
assert(response_json[:statuses].include?(DomainStatus::SERVER_UPDATE_PROHIBITED))
end
private
def auth_token
token_creator = AuthTokenCreator.create_with_defaults(@user)
hash = token_creator.token_in_hash
"Bearer #{hash[:access_token]}"
end
end

View file

@ -1,6 +1,10 @@
require 'test_helper' require 'test_helper'
class EppDomainDeleteTest < ApplicationIntegrationTest class EppDomainDeleteTest < ApplicationIntegrationTest
def setup
@domain = domains(:shop)
end
def test_bypasses_domain_and_registrant_and_contacts_validation def test_bypasses_domain_and_registrant_and_contacts_validation
request_xml = <<-XML request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -27,7 +31,9 @@ class EppDomainDeleteTest < ApplicationIntegrationTest
end end
def test_discarded_domain_cannot_be_deleted def test_discarded_domain_cannot_be_deleted
domains(:shop).discard travel_to Time.zone.parse('2010-07-05 10:30')
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
@domain.discard
request_xml = <<-XML request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -51,5 +57,6 @@ class EppDomainDeleteTest < ApplicationIntegrationTest
post '/epp/command/delete', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames' post '/epp/command/delete', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames'
end end
assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code]
travel_back
end end
end end

View file

@ -1,6 +1,10 @@
require 'test_helper' require 'test_helper'
class EppDomainUpdateTest < ApplicationIntegrationTest class EppDomainUpdateTest < ApplicationIntegrationTest
def setup
@domain = domains(:shop)
end
def test_update_domain def test_update_domain
request_xml = <<-XML request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -21,13 +25,16 @@ class EppDomainUpdateTest < ApplicationIntegrationTest
XML XML
post '/epp/command/update', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames' post '/epp/command/update', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames'
assert_equal 'f0ff7d17b0', domains(:shop).transfer_code @domain.reload
assert_equal 'f0ff7d17b0', @domain.transfer_code
assert_equal '1000', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '1000', Nokogiri::XML(response.body).at_css('result')[:code]
assert_equal 1, Nokogiri::XML(response.body).css('result').size assert_equal 1, Nokogiri::XML(response.body).css('result').size
end end
def test_discarded_domain_cannot_be_updated def test_discarded_domain_cannot_be_updated
domains(:shop).discard travel_to Time.zone.parse('2010-07-05 10:30')
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
@domain.discard
request_xml = <<-XML request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -44,5 +51,6 @@ class EppDomainUpdateTest < ApplicationIntegrationTest
post '/epp/command/update', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames' post '/epp/command/update', { frame: request_xml }, 'HTTP_COOKIE' => 'session=api_bestnames'
assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code]
travel_back
end end
end end

View file

@ -1,12 +1,17 @@
require 'test_helper' require 'test_helper'
class EppDomainTransferRequestTest < ApplicationIntegrationTest class EppDomainTransferRequestTest < ApplicationIntegrationTest
setup do def setup
@domain = domains(:shop) @domain = domains(:shop)
@new_registrar = registrars(:goodnames) @new_registrar = registrars(:goodnames)
@original_transfer_wait_time = Setting.transfer_wait_time
Setting.transfer_wait_time = 0 Setting.transfer_wait_time = 0
end end
def teardown
Setting.transfer_wait_time = @original_transfer_wait_time
end
def test_transfers_domain_at_once def test_transfers_domain_at_once
post '/epp/command/transfer', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=api_goodnames' } post '/epp/command/transfer', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=api_goodnames' }
assert_equal '1000', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '1000', Nokogiri::XML(response.body).at_css('result')[:code]
@ -75,14 +80,17 @@ class EppDomainTransferRequestTest < ApplicationIntegrationTest
assert_equal '2304', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '2304', Nokogiri::XML(response.body).at_css('result')[:code]
end end
def test_discarded_domain def test_discarded_domain_cannot_be_transferred
@domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) travel_to Time.zone.parse('2010-07-05 10:30')
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
@domain.discard
post '/epp/command/transfer', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=api_goodnames' } post '/epp/command/transfer', { frame: request_xml }, { 'HTTP_COOKIE' => 'session=api_goodnames' }
@domain.reload @domain.reload
assert_equal registrars(:bestnames), @domain.registrar assert_equal registrars(:bestnames), @domain.registrar
assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code] assert_equal '2105', Nokogiri::XML(response.body).at_css('result')[:code]
travel_back
end end
def test_same_registrar def test_same_registrar

View file

@ -0,0 +1,51 @@
require 'test_helper'
class DiscardDomainTaskTest < TaskTestCase
setup do
travel_to Time.zone.parse('2010-07-05 08:00')
@domain = domains(:shop)
end
def test_discard_domains_with_past_delete_at
@domain.update!(delete_at: Time.zone.parse('2010-07-05 07:59'))
Rake::Task['domain:discard'].execute
@domain.reload
assert @domain.discarded?
end
def test_ignore_domains_with_delete_at_in_the_future_or_now
@domain.update!(delete_at: Time.zone.parse('2010-07-05 08:00'))
Rake::Task['domain:discard'].execute
@domain.reload
refute @domain.discarded?
end
def test_ignore_already_discarded_domains
@domain.update!(delete_at: Time.zone.parse('2010-07-05 07:59'))
@domain.discard
job_count = lambda do
QueJob.where("args->>0 = '#{@domain.id}'", job_class: DomainDeleteJob.name).count
end
assert_no_difference job_count, 'A domain should not be discarded again' do
Rake::Task['domain:discard'].execute
end
end
def test_ignore_domains_with_server_delete_prohibited_status
@domain.update!(delete_at: Time.zone.parse('2010-07-05 07:59'),
statuses: [DomainStatus::SERVER_DELETE_PROHIBITED])
Rake::Task['domain:discard'].execute
@domain.reload
refute @domain.discarded?
end
def test_show_results
@domain.update!(delete_at: Time.zone.parse('2010-07-05 07:59'))
$stdout = StringIO.new
Rake::Task['domain:discard'].execute
assert_equal "shop.test is discarded\nDiscarded total: 1\n", $stdout.string
end
end

View file

@ -1,14 +0,0 @@
require 'test_helper'
class DomainDeletableTest < ActiveSupport::TestCase
setup do
@domain = domains(:shop)
end
def test_discard
refute @domain.discarded?
@domain.discard
@domain.reload
assert @domain.discarded?
end
end

View file

@ -0,0 +1,63 @@
require 'test_helper'
class DomainDiscardableTest < ActiveSupport::TestCase
setup do
travel_to Time.zone.parse('2010-07-05 10:30')
@domain = domains(:shop)
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
end
teardown do
travel_back
end
def test_discarding_a_domain_persists_the_state
@domain.discard
@domain.reload
assert @domain.discarded?
end
def test_discarding_a_domain_schedules_deletion_at_random_time
@domain.discard
other_domain = domains(:airport)
other_domain.delete_at = Time.zone.parse('2010-07-04')
other_domain.discard
background_job = QueJob.find_by("args->>0 = '#{@domain.id}'", job_class: DomainDeleteJob.name)
other_background_job = QueJob.find_by("args->>0 = '#{other_domain.id}'",
job_class: DomainDeleteJob.name)
assert_not_equal background_job.run_at, other_background_job.run_at
end
def test_discarding_a_domain_bypasses_validation
domain = domains(:invalid)
domain.delete_at = Time.zone.parse('2010-07-05 10:00')
domain.discard
domain.reload
assert domain.discarded?
end
def test_domain_cannot_be_discarded_repeatedly
@domain.discard
exception = assert_raises do
@domain.discard
end
assert_equal 'Domain is already discarded', exception.message
end
def test_keeping_a_domain_bypasses_validation
domain = domains(:invalid)
domain.delete_at = Time.zone.parse('2010-07-05 10:00')
domain.discard
domain.keep
domain.reload
assert_not domain.discarded?
end
def test_keeping_a_domain_cancels_domain_deletion
@domain.discard
@domain.keep
assert_nil QueJob.find_by("args->>0 = '#{@domain.id}'", job_class: DomainDeleteJob.name)
end
end

View file

@ -0,0 +1,52 @@
require 'test_helper'
class DomainVersionTest < ActiveSupport::TestCase
def setup
super
@domain = domains(:shop)
@contacts = @domain.contacts
@user = users(:registrant)
end
def teardown
super
end
def test_assigns_creator_to_paper_trail_whodunnit
duplicate_domain = prepare_duplicate_domain
PaperTrail.whodunnit = @user.id_role_username
assert_difference 'duplicate_domain.versions.count', 1 do
duplicate_domain.save!
end
assert_equal(duplicate_domain.creator, @user)
assert_equal(duplicate_domain.updator, @user)
assert_equal(duplicate_domain.creator_str, @user.id_role_username)
assert_equal(duplicate_domain.updator_str, @user.id_role_username)
end
def test_assigns_updator_to_paper_trail_whodunnit
PaperTrail.whodunnit = @user.id_role_username
assert_difference '@domain.versions.count', 1 do
@domain.apply_registry_lock
end
assert_equal(@domain.updator, @user)
assert_equal(@domain.updator_str, @user.id_role_username)
end
private
def prepare_duplicate_domain
duplicate_domain = @domain.dup
duplicate_domain.tech_contacts << @contacts
duplicate_domain.admin_contacts << @contacts
duplicate_domain.name = 'duplicate.test'
duplicate_domain.uuid = nil
duplicate_domain
end
end

View file

@ -1,7 +1,7 @@
require 'test_helper' require 'test_helper'
class DomainForceDeleteTest < ActiveSupport::TestCase class DomainForceDeleteTest < ActiveSupport::TestCase
def setup setup do
@domain = domains(:shop) @domain = domains(:shop)
end end
@ -74,7 +74,7 @@ class DomainForceDeleteTest < ActiveSupport::TestCase
end end
def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded
@domain.discard @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE])
assert_raises StandardError do assert_raises StandardError do
@domain.schedule_force_delete @domain.schedule_force_delete
end end

View file

@ -0,0 +1,72 @@
require 'test_helper'
class DomainRegistryLockableTest < ActiveSupport::TestCase
def setup
super
@domain = domains(:airport)
end
def test_registry_lock_on_lockable_domain
refute(@domain.locked_by_registrant?)
@domain.apply_registry_lock
assert_equal(
[DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::SERVER_TRANSFER_PROHIBITED],
@domain.statuses
)
assert(@domain.locked_by_registrant?)
assert(@domain.locked_by_registrant_at)
end
def test_registry_lock_cannot_be_applied_twice
@domain.apply_registry_lock
refute(@domain.apply_registry_lock)
assert(@domain.locked_by_registrant?)
assert(@domain.locked_by_registrant_at)
end
def test_registry_lock_cannot_be_applied_on_pending_statuses
@domain.statuses << DomainStatus::PENDING_RENEW
refute(@domain.apply_registry_lock)
refute(@domain.locked_by_registrant?)
refute(@domain.locked_by_registrant_at)
end
def test_remove_registry_lock_on_locked_domain
@domain.apply_registry_lock
assert_equal(
[DomainStatus::SERVER_UPDATE_PROHIBITED,
DomainStatus::SERVER_DELETE_PROHIBITED,
DomainStatus::SERVER_TRANSFER_PROHIBITED],
@domain.statuses
)
@domain.remove_registry_lock
assert_equal(["ok"], @domain.statuses)
refute(@domain.locked_by_registrant?)
refute(@domain.locked_by_registrant_at)
end
def test_remove_registry_lock_on_non_locked_domain
refute(@domain.locked_by_registrant?)
refute(@domain.remove_registry_lock)
assert_equal([], @domain.statuses)
refute(@domain.locked_by_registrant?)
refute(@domain.locked_by_registrant_at)
end
def test_registry_lock_cannot_be_removed_if_statuses_were_set_by_admin
@domain.statuses << DomainStatus::SERVER_UPDATE_PROHIBITED
@domain.statuses << DomainStatus::SERVER_DELETE_PROHIBITED
@domain.statuses << DomainStatus::SERVER_TRANSFER_PROHIBITED
refute(@domain.remove_registry_lock)
end
end

View file

@ -0,0 +1,56 @@
require 'test_helper'
class RegistrantUserCreationTest < ActiveSupport::TestCase
def test_find_or_create_by_api_data_creates_a_user
user_data = {
ident: '37710100070',
first_name: 'JOHN',
last_name: 'SMITH'
}
RegistrantUser.find_or_create_by_api_data(user_data)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
def test_find_or_create_by_api_data_creates_a_user_after_upcasing_input
user_data = {
ident: '37710100070',
first_name: 'John',
last_name: 'Smith'
}
RegistrantUser.find_or_create_by_api_data(user_data)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
def test_find_or_create_by_mid_data_creates_a_user
user_data = OpenStruct.new(user_country: 'EE', user_id_code: '37710100070',
user_givenname: 'JOHN', user_surname: 'SMITH')
RegistrantUser.find_or_create_by_mid_data(user_data)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
def test_find_or_create_by_idc_with_legacy_header_creates_a_user
header = '/C=EE/O=ESTEID/OU=authentication/CN=SMITH,JOHN,37710100070/SN=SMITH/GN=JOHN/serialNumber=37710100070'
RegistrantUser.find_or_create_by_idc_data(header, RegistrantUser::ACCEPTED_ISSUER)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
def test_find_or_create_by_idc_with_rfc2253_header_creates_a_user
header = 'serialNumber=37710100070,GN=JOHN,SN=SMITH,CN=SMITH\\,JOHN\\,37710100070,OU=authentication,O=ESTEID,C=EE'
RegistrantUser.find_or_create_by_idc_data(header, RegistrantUser::ACCEPTED_ISSUER)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
end

View file

@ -1,62 +1,38 @@
require 'test_helper'
class RegistrantUserTest < ActiveSupport::TestCase class RegistrantUserTest < ActiveSupport::TestCase
def setup def setup
super super
@user = users(:registrant)
end end
def teardown def teardown
super super
end end
def test_find_or_create_by_api_data_creates_a_user def test_domains_returns_an_list_of_distinct_domains_associated_with_a_specific_id_code
user_data = { domain_names = @user.domains.pluck(:name)
ident: '37710100070', assert_equal(4, domain_names.length)
first_name: 'JOHN',
last_name: 'SMITH'
}
RegistrantUser.find_or_create_by_api_data(user_data) # User is a registrant, but not a contact for the domain. Should be included in the list.
assert(domain_names.include?('shop.test'))
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end end
def test_find_or_create_by_api_data_creates_a_user_after_upcasing_input def test_administered_domains_returns_a_list_of_domains
user_data = { domain_names = @user.administered_domains.pluck(:name)
ident: '37710100070', assert_equal(3, domain_names.length)
first_name: 'John',
last_name: 'Smith'
}
RegistrantUser.find_or_create_by_api_data(user_data) # User is a tech contact for the domain.
refute(domain_names.include?('library.test'))
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end end
def test_find_or_create_by_mid_data_creates_a_user def test_contacts_returns_an_list_of_contacts_associated_with_a_specific_id_code
user_data = OpenStruct.new(user_country: 'EE', user_id_code: '37710100070', assert_equal(1, @user.contacts.count)
user_givenname: 'JOHN', user_surname: 'SMITH')
RegistrantUser.find_or_create_by_mid_data(user_data)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end end
def test_find_or_create_by_idc_with_legacy_header_creates_a_user def test_ident_and_country_code_helper_methods
header = '/C=EE/O=ESTEID/OU=authentication/CN=SMITH,JOHN,37710100070/SN=SMITH/GN=JOHN/serialNumber=37710100070' assert_equal('1234', @user.ident)
assert_equal('US', @user.country_code)
RegistrantUser.find_or_create_by_idc_data(header, RegistrantUser::ACCEPTED_ISSUER)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end
def test_find_or_create_by_idc_with_rfc2253_header_creates_a_user
header = 'serialNumber=37710100070,GN=JOHN,SN=SMITH,CN=SMITH\\,JOHN\\,37710100070,OU=authentication,O=ESTEID,C=EE'
RegistrantUser.find_or_create_by_idc_data(header, RegistrantUser::ACCEPTED_ISSUER)
user = User.find_by(registrant_ident: 'EE-37710100070')
assert_equal('JOHN SMITH', user.username)
end end
end end

View file

@ -21,8 +21,8 @@ class ContactVersionsTest < ApplicationSystemTestCase
VALUES (75, 'test_registrar', 'test123', 'test@test.com', 'EE', 'TEST123', VALUES (75, 'test_registrar', 'test123', 'test@test.com', 'EE', 'TEST123',
'test123', 'en'); 'test123', 'en');
INSERT INTO contacts (id, code, auth_info, registrar_id) INSERT INTO contacts (id, code, email, auth_info, registrar_id)
VALUES (75, 'test_code', '8b4d462aa04194ca78840a', 75); VALUES (75, 'test_code', 'test@inbox.test', '8b4d462aa04194ca78840a', 75);
INSERT INTO log_contacts (item_type, item_id, event, whodunnit, object, INSERT INTO log_contacts (item_type, item_id, event, whodunnit, object,
object_changes, created_at, session, children, ident_updated_at, uuid) object_changes, created_at, session, children, ident_updated_at, uuid)

View file

@ -21,8 +21,8 @@ class DomainVersionsTest < ApplicationSystemTestCase
VALUES (54, 'test_registrar', 'test123', 'test@test.com', 'EE', 'TEST123', VALUES (54, 'test_registrar', 'test123', 'test@test.com', 'EE', 'TEST123',
'test123', 'en'); 'test123', 'en');
INSERT INTO contacts (id, code, auth_info, registrar_id) INSERT INTO contacts (id, code, email, auth_info, registrar_id)
VALUES (54, 'test_code', '8b4d462aa04194ca78840a', 54); VALUES (54, 'test_code', 'test@inbox.test', '8b4d462aa04194ca78840a', 54);
INSERT INTO domains (id, registrar_id, valid_to, registrant_id, INSERT INTO domains (id, registrar_id, valid_to, registrant_id,
transfer_code) transfer_code)

View file

@ -7,9 +7,14 @@ class AdminAreaDomainDetailsTest < ApplicationSystemTestCase
end end
def test_discarded_domain_has_corresponding_label def test_discarded_domain_has_corresponding_label
travel_to Time.zone.parse('2010-07-05 10:30')
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
visit admin_domain_url(@domain) visit admin_domain_url(@domain)
assert_no_css 'span.label.label-warning', text: 'deleteCandidate' assert_no_css 'span.label.label-warning', text: 'deleteCandidate'
@domain.discard @domain.discard
visit admin_domain_url(@domain) visit admin_domain_url(@domain)
assert_css 'span.label.label-warning', text: 'deleteCandidate' assert_css 'span.label.label-warning', text: 'deleteCandidate'
end end

View file

@ -56,7 +56,7 @@ class AdminAreaDomainForceDeleteTest < ApplicationSystemTestCase
end end
def test_force_delete_procedure_cannot_be_scheduled_on_a_discarded_domain def test_force_delete_procedure_cannot_be_scheduled_on_a_discarded_domain
@domain.discard @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE])
visit edit_admin_domain_url(@domain) visit edit_admin_domain_url(@domain)
assert_no_button 'Schedule force delete' assert_no_button 'Schedule force delete'

View file

@ -0,0 +1,57 @@
require 'test_helper'
class AdminAreaRegistryLockTest < JavaScriptApplicationSystemTestCase
def setup
super
WebMock.allow_net_connect!
sign_in users(:admin)
travel_to Time.zone.parse('2010-07-05 00:30:00')
@domain = domains(:airport)
end
def teardown
travel_back
end
def test_does_not_have_link_when_domain_is_not_locked
visit edit_admin_domain_path(@domain)
click_link_or_button('Actions')
refute(page.has_link?('Remove registry lock'))
end
def test_can_remove_registry_lock_from_a_domain
@domain.apply_registry_lock
visit edit_admin_domain_path(@domain)
click_link_or_button('Actions')
assert(page.has_link?('Remove registry lock'))
accept_confirm('Are you sure you want to remove the registry lock?') do
click_link_or_button('Remove registry lock')
end
assert_text('Registry lock removed')
@domain.reload
refute @domain.locked_by_registrant?
end
def test_cannot_remove_registry_lock_from_not_locked_domain
@domain.apply_registry_lock
visit edit_admin_domain_path(@domain)
@domain.remove_registry_lock
refute @domain.locked_by_registrant?
click_link_or_button('Actions')
assert(page.has_link?('Remove registry lock'))
accept_confirm('Are you sure you want to remove the registry lock?') do
click_link_or_button('Remove registry lock')
end
assert_text('Registry lock could not be removed')
refute @domain.locked_by_registrant?
end
end

View file

@ -3,11 +3,41 @@ require 'test_helper'
class AdminDomainsTestTest < ApplicationSystemTestCase class AdminDomainsTestTest < ApplicationSystemTestCase
setup do setup do
sign_in users(:admin) sign_in users(:admin)
travel_to Time.zone.parse('2010-07-05 00:30:00')
@domain = domains(:shop)
end
teardown do
travel_back
end end
def test_shows_details def test_shows_details
domain = domains(:shop) visit admin_domain_path(@domain)
visit admin_domain_path(domain) assert_field nil, with: @domain.transfer_code
assert_field nil, with: domain.transfer_code end
def test_admin_registry_lock_date
visit admin_domain_path(@domain)
refute_text 'Registry lock time 2010-07-05 00:30'
lockable_domain = domains(:airport)
lockable_domain.apply_registry_lock
visit admin_domain_path(lockable_domain)
assert_text 'Registry lock time 2010-07-05 00:30'
assert_text 'registryLock'
end
def test_keep_a_domain
@domain.delete_at = Time.zone.parse('2010-07-05 10:00')
@domain.discard
visit edit_admin_domain_url(@domain)
click_link_or_button 'Remove deleteCandidate status'
@domain.reload
assert_not @domain.discarded?
assert_text 'deleteCandidate status has been removed'
assert_no_link 'Remove deleteCandidate status'
end end
end end