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

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

View file

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

View file

@ -6,6 +6,7 @@ module Api
module Registrant
class BaseController < ActionController::API
before_action :authenticate
before_action :set_paper_trail_whodunnit
rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
error = {}
@ -22,16 +23,32 @@ module Api
header.gsub(pattern, '') if header&.match(pattern)
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
decryptor = AuthTokenDecryptor.create_with_defaults(bearer_token)
decryptor.decrypt_token
if decryptor.valid?
sign_in decryptor.user
sign_in(:registrant_user, decryptor.user)
else
render json: { errors: [{base: ['Not authorized']}] }, status: :unauthorized
render json: { errors: [{ base: ['Not authorized'] }] },
status: :unauthorized
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

View file

@ -30,17 +30,6 @@ module Api
render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found
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

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
extend ActiveSupport::Concern
included do
alias_attribute :delete_time, :delete_at
private
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
def discard
statuses << DomainStatus::DELETE_CANDIDATE
save
def do_not_delete_later
# Que job can be manually deleted in admin area UI
QueJob.find_by("args->>0 = '#{id}'", job_class: DomainDeleteJob.name)&.destroy
end
def discarded?
statuses.include?(DomainStatus::DELETE_CANDIDATE)
def deletion_time_span
range_params = [Time.zone.now.to_i, deletion_deadline.to_i].sort
Range.new(*range_params)
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)
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

View file

@ -4,8 +4,10 @@ class Domain < ActiveRecord::Base
include Concerns::Domain::Expirable
include Concerns::Domain::Activatable
include Concerns::Domain::ForceDelete
include Concerns::Domain::Discardable
include Concerns::Domain::Deletable
include Concerns::Domain::Transferable
include Concerns::Domain::RegistryLockable
has_paper_trail class_name: "DomainVersion", meta: { children: :children_log }
@ -249,13 +251,6 @@ class Domain < ActiveRecord::Base
true
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?
if Setting.days_to_renew_domain_before_expire != 0
# 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)
end
def self.delete_candidates
where("#{attribute_alias(:delete_time)} < ?", Time.zone.now)
end
def self.uses_zone?(zone)
exists?(["name ILIKE ?", "%.#{zone.origin}"])
end

View file

@ -84,22 +84,6 @@ class DomainCron
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|
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?

View file

@ -153,13 +153,11 @@ class DomainStatus < ActiveRecord::Base
[
['Hold', SERVER_HOLD],
['ManualInzone', SERVER_MANUAL_INZONE],
# [''],
['RenewProhibited', SERVER_RENEW_PROHIBITED],
['TransferProhibited', SERVER_TRANSFER_PROHIBITED],
['RegistrantChangeProhibited', SERVER_REGISTRANT_CHANGE_PROHIBITED],
['AdminChangeProhibited', SERVER_ADMIN_CHANGE_PROHIBITED],
['TechChangeProhibited', SERVER_TECH_CHANGE_PROHIBITED],
# [''],
['UpdateProhibited', SERVER_UPDATE_PROHIBITED],
['DeleteProhibited', SERVER_DELETE_PROHIBITED]
]
@ -171,11 +169,11 @@ class DomainStatus < ActiveRecord::Base
INACTIVE,
FORCE_DELETE,
PENDING_CREATE,
#PENDING_DELETE,
PENDING_RENEW,
PENDING_TRANSFER,
PENDING_UPDATE,
PENDING_DELETE_CONFIRMATION
PENDING_DELETE_CONFIRMATION,
DELETE_CANDIDATE,
]
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
ACCEPTED_ISSUER = 'AS Sertifitseerimiskeskus'
ACCEPTED_ISSUER = 'AS Sertifitseerimiskeskus'.freeze
attr_accessor :idc_data
devise :database_authenticatable, :trackable, :timeoutable
@ -10,16 +10,46 @@ class RegistrantUser < User
delegate :can?, :cannot?, to: :ability
def ident
registrant_ident.to_s.split("-").last
registrant_ident.to_s.split('-').last
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
ident_cc, ident = registrant_ident.to_s.split '-'
Domain.includes(:registrar, :registrant).where(contacts: {
ident_type: 'priv',
ident: ident, #identity_code,
ident_country_code: ident_cc #country_code
})
domains_where_is_contact = begin
Domain.joins(:domain_contacts)
.where(domain_contacts: { contact_id: contacts })
end
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
def to_s
@ -35,13 +65,13 @@ class RegistrantUser < User
user_data = {}
# 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[:country_code] = idc_data.scan(/^\/C=(.{2})/).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
else
parse_str = "," + idc_data
parse_str = ',' + idc_data
user_data[:ident] = parse_str.scan(/,serialNumber=(\d+)/).flatten.first
user_data[:country_code] = parse_str.scan(/,C=(.{2})/).flatten.first
user_data[:first_name] = parse_str.scan(/,GN=([^,]+)/).flatten.first

View file

@ -14,6 +14,11 @@ class DomainPresenter
html += " #{label}"
end
if domain.locked_by_registrant?
label = view.content_tag(:span, 'registryLock', class: 'label label-danger')
html += " #{label}"
end
html.html_safe
end
@ -30,7 +35,7 @@ class DomainPresenter
end
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
def force_delete_date
@ -59,11 +64,30 @@ class DomainPresenter
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
def schedule_force_delete_btn
view.content_tag(:a, view.t('admin.domains.force_delete_toggle_btn.schedule'),
class: 'btn btn-default',
class: 'dropdown-item',
data: {
toggle: 'modal',
target: '.domain-edit-force-delete-dialog',
@ -77,14 +101,14 @@ class DomainPresenter
data: {
confirm: view.t('admin.domains.force_delete_toggle_btn.cancel_confirm'),
},
class: 'btn btn-primary'
class: 'dropdown-item'
end
def inactive_schedule_force_delete_btn
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'),
disabled: true,
class: 'btn btn-default'
class: 'dropdown-item'
end
attr_reader :domain

View file

@ -5,18 +5,31 @@
<li><%= link_to @domain, admin_domain_path(@domain) %></li>
</ol>
<div class="page-header">
<div class="row">
<div class="col-sm-4">
<h1><%= t '.header', domain: domain.name %></h1>
<div class="col-sm-8">
<h1 class="text-center-xs">
<%= t '.header' %> <%= domain.name_with_status %>
</h1>
</div>
<div class="col-sm-8 text-right">
<%= link_to t('.add_new_status_btn'), '#', class: 'btn btn-primary js-add-status' %>
<%= domain.force_delete_toggle_btn %>
<div class="col-sm-4 text-right">
<div class="btn-group">
<button class="btn btn-primary" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
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>
<%= 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>
<dd><%= l @domain.force_delete_at %></dd>
<dt><%= t('.locked_by_registrant_at') %></dt>
<dd><%= l(@domain.locked_by_registrant_at) %></dd>
</dl>
</div>
</div>