Updated REPP API for new registrar portal

This commit is contained in:
Sergei Tsõganov 2022-06-06 13:43:30 +03:00
parent e17b21436d
commit a5ffce290d
61 changed files with 1269 additions and 408 deletions

View file

@ -0,0 +1,149 @@
module Repp
module V1
class AccountController < BaseController
load_and_authorize_resource
api :get, '/repp/v1/account'
desc 'Get all activities'
def index
records = current_user.registrar.cash_account.activities
q = records.ransack(search_params)
q.sorts = 'created_at desc' if q.sorts.empty?
activities = q.result(distinct: true)
limited_activities = activities.limit(limit).offset(offset)
.includes(:invoice)
render_success(data: { activities: serialized_activities(limited_activities),
count: activities.count,
types_for_select: AccountActivity.types_for_select })
end
api :get, '/repp/v1/account/details'
desc 'Get current registrar account details'
def details
registrar = current_user.registrar
type = registrar.settings['balance_auto_reload']&.dig('type')
resp = { account: { billing_email: registrar.billing_email,
iban: registrar.iban,
iban_max_length: Iban.max_length,
linked_users: serialized_users(current_user.linked_users),
balance_auto_reload: type,
min_deposit: Setting.minimum_deposit } }
render_success(data: resp)
end
api :put, '/repp/v1/account'
desc 'Update current registrar account details'
def update
registrar = current_user.registrar
unless registrar.update(account_params)
handle_non_epp_errors(registrar)
return
end
render_success(data: { account: account_params },
message: I18n.t('registrar.account.update.saved'))
end
api :post, '/repp/v1/account/update_auto_reload_balance'
desc 'Enable current registrar balance auto reload'
def update_auto_reload_balance
type = BalanceAutoReloadTypes::Threshold.new(type_params)
unless type.valid?
handle_non_epp_errors(type)
return
end
settings = { balance_auto_reload: { type: type.as_json } }
current_user.registrar.update!(settings: settings)
render_success(data: { settings: settings },
message: I18n.t('registrar.settings.balance_auto_reload.update.saved'))
end
api :get, '/repp/v1/account/disable_auto_reload_balance'
desc 'Disable current registrar balance auto reload'
def disable_auto_reload_balance
registrar = current_user.registrar
registrar.settings.delete('balance_auto_reload')
registrar.save!
render_success(data: { settings: registrar.settings },
message: I18n.t('registrar.settings.balance_auto_reload.destroy.disabled'))
end
api :get, '/repp/v1/account/balance'
desc "Get account's balance"
def balance
resp = { balance: current_user.registrar.cash_account.balance,
currency: current_user.registrar.cash_account.currency }
if params[:detailed] == 'true'
activities = current_user.registrar.cash_account.activities.order(created_at: :desc)
activities = activities.where('created_at >= ?', params[:from]) if params[:from]
activities = activities.where('created_at <= ?', params[:until]) if params[:until]
resp[:transactions] = serialized_activities(activities)
end
render_success(data: resp)
end
private
def account_params
params.require(:account).permit(:billing_email, :iban)
end
def index_params
params.permit(:id, :limit, :offset, :q,
:page, :per_page,
q: [:description_matches, :created_at_gteq,
:created_at_lteq, :s, { s: [] }, { activity_type_in: [] }])
end
def type_params
permitted_params = params.require(:type).permit(:amount, :threshold)
normalize_params(permitted_params)
end
def normalize_params(params)
params[:amount] = params[:amount].to_f
params[:threshold] = params[:threshold].to_f
params
end
def search_params
index_params.fetch(:q, {})
end
def limit
index_params[:limit] || 200
end
def offset
index_params[:offset] || 0
end
def serialized_users(users)
arr = []
users.each do |u|
arr << { id: u.id, username: u.username,
role: u.roles.first }
end
arr
end
def serialized_activities(activities)
arr = []
activities.each do |a|
arr << { created_at: a.created_at, description: a.description,
type: a.activity_type == 'add_credit' ? 'credit' : 'debit',
sum: a.sum, balance: a.new_balance, currency: a.currency,
updator: a.updator_str }
end
arr
end
end
end
end

View file

@ -1,33 +0,0 @@
module Repp
module V1
class AccountsController < BaseController
api :GET, '/repp/v1/accounts/balance'
desc "Get account's balance"
def balance
resp = { balance: current_user.registrar.cash_account.balance,
currency: current_user.registrar.cash_account.currency }
resp[:transactions] = activities if params[:detailed] == 'true'
render_success(data: resp)
end
def activities
arr = []
registrar_activities.each do |a|
arr << { created_at: a.created_at, description: a.description,
type: a.activity_type == 'add_credit' ? 'credit' : 'debit',
sum: a.sum, balance: a.new_balance }
end
arr
end
def registrar_activities
activities = current_user.registrar.cash_account.activities.order(created_at: :desc)
activities = activities.where('created_at >= ?', params[:from]) if params[:from]
activities = activities.where('created_at <= ?', params[:until]) if params[:until]
activities
end
end
end
end

View file

@ -1,12 +1,12 @@
module Repp
module V1
class BaseController < ActionController::API # rubocop:disable Metrics/ClassLength
attr_reader :current_user
around_action :log_request
before_action :authenticate_user
before_action :validate_webclient_ca
before_action :check_ip_restriction
attr_reader :current_user
before_action :set_paper_trail_whodunnit
private
@ -22,6 +22,10 @@ module Repp
rescue Apipie::ParamInvalid => e
@response = { code: 2005, message: e.message.gsub(/\n/, '. ') }
render(json: @response, status: :bad_request)
rescue CanCan::AccessDenied => e
@response = { code: 2201, message: 'Authorization error' }
logger.error e.to_s
render(json: @response, status: :unauthorized)
ensure
create_repp_log
end
@ -65,7 +69,6 @@ module Repp
def handle_errors(obj = nil)
@epp_errors ||= ActiveModel::Errors.new(self)
if obj
obj.construct_epp_errors
obj.errors.each { |error| @epp_errors.import error }
@ -85,6 +88,12 @@ module Repp
render(json: @response, status: status)
end
def handle_non_epp_errors(obj, message = nil)
@response = { message: message || obj.errors.full_messages.join(', '),
data: {} }
render(json: @response, status: :bad_request)
end
def basic_token
pattern = /^Basic /
header = request.headers['Authorization']
@ -94,7 +103,8 @@ module Repp
def authenticate_user
username, password = Base64.urlsafe_decode64(basic_token).split(':')
@current_user ||= ApiUser.find_by(username: username, plain_text_password: password)
@current_user ||= ApiUser.find_by(username: username, plain_text_password: password,
active: true)
return if @current_user
@ -123,6 +133,7 @@ module Repp
return unless webclient_request?
request_name = request.env['HTTP_SSL_CLIENT_S_DN_CN']
webclient_cn = ENV['webclient_cert_common_name'] || 'webclient'
return if request_name == webclient_cn
@ -135,6 +146,16 @@ module Repp
def logger
Rails.logger
end
def auth_values_to_data(registrar:)
data = current_user.as_json(only: %i[id username roles])
data[:registrar_name] = registrar.name
data[:legaldoc_mandatory] = registrar.legaldoc_mandatory?
data[:balance] = { amount: registrar.cash_account&.balance,
currency: registrar.cash_account&.currency }
data[:abilities] = Ability.new(current_user).permissions
data
end
end
end
end

View file

@ -3,23 +3,61 @@ module Repp
module V1
class ContactsController < BaseController # rubocop:disable Metrics/ClassLength
before_action :find_contact, only: %i[show update destroy]
skip_around_action :log_request, only: :search
api :get, '/repp/v1/contacts'
desc 'Get all existing contacts'
def index
record_count = current_user.registrar.contacts.count
contacts = showable_contacts(params[:details], params[:limit] || 200,
params[:offset] || 0)
@response = { contacts: contacts, total_number_of_records: record_count }
render(json: @response, status: :ok)
authorize! :check, Epp::Contact
records = current_user.registrar.contacts.order(created_at: :desc)
q = records.ransack(search_params)
q.sorts = 'created_at desc' if q.sorts.empty?
contacts = q.result(distinct: true)
limited_contacts = contacts.limit(limit).offset(offset)
.includes(:domain_contacts, :registrant_domains, :registrar)
render_success(data: { contacts: serialized_contacts(limited_contacts),
count: contacts.count,
statuses: Contact::STATUSES,
ident_types: Contact::Ident.types })
end
# rubocop:disable Metrics/MethodLength
api :get, '/repp/v1/contacts/search(/:id)'
desc 'Search all existing contacts by optional id or query param'
def search
scope = current_user.registrar.contacts
if params[:query]
escaped_str = ActiveRecord::Base.connection.quote_string params[:query]
scope = scope.where("name ilike '%#{escaped_str}%' OR code ilike '%#{escaped_str}%'
OR ident ilike '%#{escaped_str}%'")
elsif params[:id]
scope = scope.where(code: params[:id])
end
render_success(data: scope.limit(10)
.map do |c|
{ value: c.code,
label: "#{c.code} #{c.name}",
selected: scope.size == 1 }
end)
end
# rubocop:enable Metrics/MethodLength
api :get, '/repp/v1/contacts/:contact_code'
desc 'Get a specific contact'
def show
serializer = ::Serializers::Repp::Contact.new(@contact,
show_address: Contact.address_processing?)
render_success(data: serializer.to_json)
authorize! :check, Epp::Contact
simple = params[:simple] == 'true' || false
serializer = Serializers::Repp::Contact.new(@contact,
show_address: Contact.address_processing?,
domain_params: domain_filter_params,
simplify: simple)
render_success(data: { contact: serializer.to_json })
end
api :get, '/repp/v1/contacts/check/:contact_code'
@ -35,7 +73,7 @@ module Repp
desc 'Create a new contact'
def create
@contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false)
action = Actions::ContactCreate.new(@contact, params[:legal_document],
action = Actions::ContactCreate.new(@contact, contact_params[:legal_document],
contact_ident_params)
unless action.call
@ -50,7 +88,7 @@ module Repp
desc 'Update existing contact'
def update
action = Actions::ContactUpdate.new(@contact, contact_params_with_address(required: false),
params[:legal_document],
contact_params[:legal_document],
contact_ident_params(required: false), current_user)
unless action.call
@ -73,29 +111,71 @@ module Repp
render_success
end
def contact_addr_present?
return false unless contact_addr_params.key?(:addr)
private
contact_addr_params[:addr].keys.any?
def index_params
params.permit(:id, :limit, :offset, :details, :q, :simple,
:page, :per_page, :domain_filter,
domain_filter: [],
q: %i[s name_matches code_eq ident_matches ident_type_eq
email_matches country_code_eq types_contains_array
updated_at_gteq created_at_gteq created_at_lteq
statuses_contains_array] + [s: []])
end
def search_params
index_params.fetch(:q, {})
end
def domain_filter_params
filter_params = index_params.slice(:id, :page, :per_page, :domain_filter).to_h
filter_params.merge!({ sort: hashify(index_params[:q].fetch(:s)) }) if index_params[:q]
filter_params
end
def hashify(sort)
return unless sort
sort_hash = {}
if sort.is_a?(Array)
sort.each do |s|
sort_hash.merge!(Hash[*s.split(' ')])
end
else
sort_hash.merge!(Hash[*sort.split(' ')])
end
sort_hash
end
def limit
index_params[:limit] || 200
end
def offset
index_params[:offset] || 0
end
def serialized_contacts(contacts)
return contacts.map {|c| c.code } unless index_params[:details] == 'true'
address_processing = Contact.address_processing?
contacts.map do |c|
Serializers::Repp::Contact.new(c, show_address: address_processing).to_json
end
end
def contact_addr_present?
return false unless contact_addr_params
contact_addr_params.keys.any?
end
def create_update_success_body
{ code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } },
{ code: opt_addr? ? 1100 : nil,
data: { contact: { code: @contact.code } },
message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil }
end
def showable_contacts(details, limit, offset)
contacts = current_user.registrar.contacts.limit(limit).offset(offset)
return contacts.pluck(:code) unless details
contacts.map do |contact|
serializer = ::Serializers::Repp::Contact.new(contact,
show_address: Contact.address_processing?)
serializer.to_json
end
end
def opt_addr?
!Contact.address_processing? && contact_addr_present?
end
@ -106,36 +186,36 @@ module Repp
end
def contact_params_with_address(required: true)
return contact_create_params(required: required) unless contact_addr_params.key?(:addr)
return contact_create_params(required: required) unless contact_addr_present?
addr = {}
contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] }
contact_create_params(required: required).merge(addr)
contact_create_params(required: required).merge(contact_addr_params)
end
def contact_create_params(required: true)
params.require(:contact).require(%i[name email phone]) if required
params.require(:contact).permit(:name, :email, :phone, :id)
create_params = %i[name email phone]
contact_params.require(create_params) if required
contact_params.slice(*create_params)
end
def contact_ident_params(required: true)
if required
params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code])
params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code)
else
params.permit(contact: { ident: %i[ident ident_type ident_country_code] })
end
params[:contact][:ident]
ident_params = %i[ident ident_type ident_country_code]
contact_params.require(:ident).require(ident_params) if required
contact_params[:ident].to_h
end
def contact_addr_params
if Contact.address_processing?
params.require(:contact).require(:addr).require(%i[country_code city street zip])
params.require(:contact).require(:addr).permit(:country_code, :city, :street, :zip)
else
params.require(:contact).permit(addr: %i[country_code city street zip])
end
return contact_params[:addr] unless Contact.address_processing?
addr_params = %i[country_code city street zip]
contact_params.require(:addr).require(addr_params)
contact_params[:addr]
end
def contact_params
params.require(:contact).permit(:name, :email, :phone, :legal_document,
legal_document: %i[body type],
ident: [%i[ident ident_type ident_country_code]],
addr: [%i[country_code city street zip state]])
end
end
end

View file

@ -2,19 +2,16 @@ module Repp
module V1
module Domains
class BaseContactsController < BaseController
before_action :set_current_contact, only: [:update]
before_action :set_new_contact, only: [:update]
before_action :set_contacts, 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])
def set_contacts
contacts = current_user.registrar.contacts
@current_contact = contacts.find_by!(code: contact_params[:current_contact_id])
@new_contact = contacts.find_by!(code: contact_params[:new_contact_id])
end
def update
authorize! :manage, :repp
@epp_errors ||= ActiveModel::Errors.new(self)
return unless @new_contact.invalid?
@ -26,8 +23,11 @@ module Repp
private
def contact_params
params.require(%i[current_contact_id new_contact_id])
params.permit(:current_contact_id, :new_contact_id)
param_list = %i[current_contact_id new_contact_id]
params.require(param_list)
params.permit(:current_contact_id, :new_contact_id,
contact: {},
admin_contact: [param_list])
end
end
end

View file

@ -8,28 +8,29 @@ module Repp
api :POST, 'repp/v1/domains/:domain_name/renew'
desc 'Renew domain'
param :renew, Hash, required: true, desc: 'Renew parameters' do
param :renews, Hash, required: true, desc: 'Renew parameters' do
param :period, Integer, required: true, desc: 'Renew period. Month (m) or year (y)'
param :period_unit, String, required: true, desc: 'For how many months or years to renew'
param :exp_date, String, required: true, desc: 'Current expiry date for domain'
end
def create
authorize!(:renew, @domain)
action = Actions::DomainRenew.new(@domain, renew_params[:renew], current_user.registrar)
action = Actions::DomainRenew.new(@domain, renew_params[:renews], current_user.registrar)
unless action.call
handle_errors(@domain)
return
end
render_success(data: { domain: { name: @domain.name } })
render_success(data: { domain: { name: @domain.name, id: @domain.uuid } })
end
def bulk_renew
authorize! :manage, :repp
renew = run_bulk_renew_task(@domains, bulk_renew_params[:renew_period])
return render_success(data: { updated_domains: @domains.map(&:name) }) if renew.valid?
msg = renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ')
msg = renew.errors.attribute_names.map { |k, _v| renew.errors[k] }.join(', ')
@epp_errors.add(:epp_errors, msg: msg, code: '2002')
handle_errors
end
@ -37,7 +38,7 @@ module Repp
private
def renew_params
params.permit(:domain_id, renew: %i[period period_unit exp_date])
params.permit(:domain_id, renews: %i[period period_unit exp_date])
end
def validate_renew_period
@ -53,6 +54,7 @@ module Repp
if bulk_renew_params[:domains].instance_of?(Array)
@domains = bulk_renew_domains
@epp_errors.add(:epp_errors, msg: 'Domains cannot be empty', code: '2005') if @domains.empty?
else
@epp_errors.add(:epp_errors, msg: 'Domains attribute must be an array', code: '2005')
end

View file

@ -3,6 +3,7 @@ module Repp
module V1
class DomainsController < BaseController # rubocop:disable Metrics/ClassLength
before_action :set_authorized_domain, only: %i[transfer_info destroy]
before_action :find_password, only: %i[update destroy]
before_action :validate_registrar_authorization, only: %i[transfer_info destroy]
before_action :forward_registrar_id, only: %i[create update destroy]
before_action :set_domain, only: %i[update]
@ -10,20 +11,31 @@ module Repp
api :GET, '/repp/v1/domains'
desc 'Get all existing domains'
def index
authorize! :info, Epp::Domain
records = current_user.registrar.domains
domains = records.limit(limit).offset(offset)
q = records.ransack(search_params)
q.sorts = ['valid_to asc', 'created_at desc'] if q.sorts.empty?
# use distinct: false here due to ransack bug:
# https://github.com/activerecord-hackery/ransack/issues/429
domains = q.result(distinct: false)
render_success(data: { domains: serialized_domains(domains),
total_number_of_records: records.count })
limited_domains = domains.limit(limit).offset(offset).includes(:registrar, :registrant)
render_success(data: { new_domain: records.any? ? serialized_domains([records.last]) : [],
domains: serialized_domains(limited_domains.to_a.uniq),
count: domains.count,
statuses: DomainStatus::STATUSES })
end
api :GET, '/repp/v1/domains/:domain_name'
desc 'Get a specific domain'
def show
@domain = Epp::Domain.find_by!(name: params[:id])
@domain = Epp::Domain.find_by_name(params[:id])
authorize! :info, @domain
sponsor = @domain.registrar == current_user.registrar
render_success(data: { domain: Serializers::Repp::Domain.new(@domain,
sponsored: sponsor).to_json })
serializer = Serializers::Repp::Domain.new(@domain, sponsored: sponsor)
render_success(data: { domain: serializer.to_json })
end
api :POST, '/repp/v1/domains'
@ -33,7 +45,7 @@ module Repp
param :registrant, String, required: true, desc: 'Registrant contact code'
param :reserved_pw, String, required: false, desc: 'Reserved password for domain'
param :transfer_code, String, required: false, desc: 'Desired transfer code for domain'
# param :period, String, required: true, desc: 'Registration period in months or years'
param :period, Integer, required: true, desc: 'Registration period in months or years'
param :period_unit, String, required: true, desc: 'Period type (month m) or (year y)'
param :nameservers_attributes, Array, required: false, desc: 'Domain nameservers' do
param :hostname, String, required: true, desc: 'Nameserver hostname'
@ -56,15 +68,18 @@ module Repp
end
end
def create
authorize!(:create, Epp::Domain)
authorize! :create, Epp::Domain
@domain = Epp::Domain.new
action = Actions::DomainCreate.new(@domain, domain_create_params)
action = Actions::DomainCreate.new(@domain, domain_params)
# rubocop:disable Style/AndOr
handle_errors(@domain) and return unless action.call
# rubocop:enable Style/AndOr
render_success(data: { domain: { name: @domain.name, transfer_code: @domain.transfer_code } })
render_success(data: { domain: { name: @domain.name,
transfer_code: @domain.transfer_code,
id: @domain.reload.uuid } })
end
api :PUT, '/repp/v1/domains/:domain_name'
@ -73,20 +88,20 @@ module Repp
param :domain, Hash, required: true, desc: 'Changes of domain object' do
param :registrant, Hash, required: false, desc: 'New registrant object' do
param :code, String, required: true, desc: 'New registrant contact code'
param :verified, [true, false], required: false,
desc: 'Registrant change is already verified'
param :verified, [true, false, 'true', 'false'], required: false,
desc: 'Registrant change is already verified'
end
param :transfer_code, String, required: false, desc: 'New authorization code'
end
def update
action = Actions::DomainUpdate.new(@domain, params[:domain], false)
authorize!(:update, @domain, @password)
action = Actions::DomainUpdate.new(@domain, update_params, false)
unless action.call
handle_errors(@domain)
return
end
render_success(data: { domain: { name: @domain.name } })
render_success(data: { domain: { name: @domain.name, id: @domain.uuid } })
end
api :GET, '/repp/v1/domains/:domain_name/transfer_info'
@ -108,23 +123,28 @@ module Repp
api :POST, '/repp/v1/domains/transfer'
desc 'Transfer multiple domains'
def transfer
authorize! :transfer, Epp::Domain
@errors ||= []
@successful = []
transfer_params[:domain_transfers].each do |transfer|
initiate_transfer(transfer)
end
render_success(data: { success: @successful, failed: @errors })
end
api :DELETE, '/repp/v1/domains/:domain_name'
desc 'Delete specific domain'
param :delete, Hash, required: true, desc: 'Object holding verified key' do
param :verified, [true, false], required: true,
desc: 'Whether to ask registrant verification or not'
param :id, String, desc: 'Domain name in IDN / Puny format'
param :domain, Hash, required: true, desc: 'Changes of domain object' do
param :delete, Hash, required: true, desc: 'Object holding verified key' do
param :verified, [true, false, 'true', 'false'], required: true,
desc: 'Whether to ask registrant verification or not'
end
end
def destroy
action = Actions::DomainDelete.new(@domain, params, current_user.registrar)
authorize!(:delete, @domain, @password)
action = Actions::DomainDelete.new(@domain, domain_params, current_user.registrar)
# rubocop:disable Style/AndOr
handle_errors(@domain) and return unless action.call
@ -138,7 +158,8 @@ module Repp
def serialized_domains(domains)
return domains.pluck(:name) unless index_params[:details] == 'true'
domains.map { |d| Serializers::Repp::Domain.new(d).to_json }
simple = index_params[:simple] == 'true' || false
domains.map { |d| Serializers::Repp::Domain.new(d, simplify: simple).to_json }
end
def initiate_transfer(transfer)
@ -155,18 +176,13 @@ module Repp
end
def transfer_params
params.require(:data).require(:domain_transfers).each do |t|
t.require(:domain_name)
t.permit(:domain_name)
t.require(:transfer_code)
t.permit(:transfer_code)
end
params.require(:data).permit(domain_transfers: %i[domain_name transfer_code])
params.require(:data).require(:domain_transfers)
params.require(:data).permit(domain_transfers: [%i[domain_name transfer_code]])
end
def transfer_info_params
params.require(:id)
params.permit(:id)
params.permit(:id, :legal_document, delete: [:verified])
end
def forward_registrar_id
@ -177,6 +193,7 @@ module Repp
def set_domain
registrar = current_user.registrar
@domain = Epp::Domain.find_by(registrar: registrar, name: params[:id])
@domain ||= Epp::Domain.find_by!(registrar: registrar, name_puny: params[:id])
@ -185,6 +202,10 @@ module Repp
raise ActiveRecord::RecordNotFound
end
def find_password
@password = domain_params[:transfer_code]
end
def set_authorized_domain
@epp_errors ||= ActiveModel::Errors.new(self)
@domain = domain_from_url_hash
@ -201,7 +222,7 @@ module Repp
end
def domain_from_url_hash
entry = transfer_info_params[:id]
entry = params[:id]
return Epp::Domain.find(entry) if entry.match?(/\A[0-9]+\z/)
Epp::Domain.find_by!('name = ? OR name_puny = ?', entry, entry)
@ -216,15 +237,48 @@ module Repp
end
def index_params
params.permit(:limit, :offset, :details)
params.permit(:limit, :offset, :details, :simple, :q,
q: %i[s name_matches registrant_id_eq contacts_ident_eq
nameservers_hostname_eq valid_to_gteq valid_to_lteq
statuses_contains_array] + [s: []])
end
def domain_create_params
params.require(:domain).permit(:name, :registrant, :period, :period_unit, :registrar,
:transfer_code, :reserved_pw,
dnskeys_attributes: [%i[flags alg protocol public_key]],
nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]],
admin_contacts: [], tech_contacts: [])
def search_params
index_params.fetch(:q, {})
end
def update_params
dup_params = domain_params.to_h.dup
return dup_params unless dup_params[:contacts]
new_contact_params = dup_params[:contacts].map do |c|
c.to_h.symbolize_keys
end
old_contact_params = @domain.domain_contacts.map do |c|
{ code: c.contact_code_cache, type: c.name.downcase }
end
dup_params[:contacts] = (new_contact_params - old_contact_params).map { |c| c.merge(action: 'add') }
dup_params[:contacts].concat((old_contact_params - new_contact_params)
.map { |c| c.merge(action: 'rem') })
dup_params
end
def domain_params
params.require(:domain)
.permit(:name, :period, :period_unit, :registrar,
:transfer_code, :reserved_pw, :legal_document,
:registrant, legal_document: %i[body type],
registrant: [%i[code verified]],
dns_keys: [%i[id flags alg protocol public_key action]],
nameservers: [[:id, :hostname,
:action, { ipv4: [], ipv6: [] }]],
contacts: [%i[code type action]],
nameservers_attributes: [[:hostname, { ipv4: [], ipv6: [] }]],
admin_contacts: [], tech_contacts: [],
dnskeys_attributes: [%i[flags alg protocol public_key]],
delete: [:verified])
end
end
end

View file

@ -0,0 +1,118 @@
require 'serializers/repp/invoice'
module Repp
module V1
class InvoicesController < BaseController
load_and_authorize_resource
api :get, '/repp/v1/invoices'
desc 'Get all invoices'
def index
records = current_user.registrar.invoices
q = records.ransack(search_params)
q.sorts = 'created_at desc' if q.sorts.empty?
invoices = q.result(distinct: true)
limited_invoices = invoices.limit(limit).offset(offset)
.includes(:items, :account_activity, :buyer)
render_success(data: { invoices: serialized_invoices(limited_invoices),
count: invoices.count })
end
api :get, '/repp/v1/invoices/:id'
desc 'Get a specific invoice'
def show
serializer = Serializers::Repp::Invoice.new(@invoice)
render_success(data: { invoice: serializer.to_json })
end
api :get, '/repp/v1/invoices/:id/download'
desc 'Download a specific invoice as pdf file'
def download
filename = "Invoice-#{@invoice.number}.pdf"
@response = { code: 1000, message: 'Command completed successfully',
data: filename }
send_data @invoice.as_pdf, filename: filename
end
api :post, '/repp/v1/invoices/:id/send_to_recipient'
desc 'Send invoice pdf to recipient'
def send_to_recipient
recipient = invoice_params[:recipient]
InvoiceMailer.invoice_email(invoice: @invoice, recipient: recipient)
.deliver_now
serializer = Serializers::Repp::Invoice.new(@invoice, simplify: true)
render_success(data: { invoice: serializer.to_json
.merge!(recipient: recipient) })
end
api :post, '/repp/v1/invoices/:id/cancel'
desc 'Cancel a specific invoice'
def cancel
action = Actions::InvoiceCancel.new(@invoice)
if action.call
EisBilling::SendInvoiceStatus.send_info(invoice_number: @invoice.number,
status: 'cancelled')
else
handle_non_epp_errors(@invoice)
return
end
serializer = Serializers::Repp::Invoice.new(@invoice, simplify: true)
render_success(data: { invoice: serializer.to_json })
end
api :post, '/repp/v1/invoices/add_credit'
desc 'Generate add credit invoice'
def add_credit
deposit = Deposit.new(invoice_params.merge(registrar: current_user.registrar))
invoice = deposit.issue_prepayment_invoice
if invoice
serializer = Serializers::Repp::Invoice.new(invoice, simplify: true)
render_success(data: { invoice: serializer.to_json })
else
handle_errors(deposit)
end
end
private
def index_params
params.permit(:id, :limit, :offset, :details, :q, :simple,
:page, :per_page,
q: %i[number_str_matches due_date_gteq due_date_lteq
account_activity_created_at_gteq
account_activity_created_at_lteq
account_activity_id_not_null
account_activity_id_null
cancelled_at_not_null
number_gteq number_lteq
total_gteq total_lteq s] + [s: []])
end
def search_params
index_params.fetch(:q, {})
end
def invoice_params
params.require(:invoice).permit(:id, :recipient, :amount, :description)
end
def limit
index_params[:limit] || 200
end
def offset
index_params[:offset] || 0
end
def serialized_invoices(invoices)
return invoices.pluck(:number) unless index_params[:details] == 'true'
simple = index_params[:simple] == 'true' || false
invoices.map { |i| Serializers::Repp::Invoice.new(i, simplify: simple).to_json }
end
end
end
end

View file

@ -0,0 +1,49 @@
module Repp
module V1
module Registrar
class AuthController < BaseController
skip_before_action :authenticate_user, only: :tara_callback
skip_before_action :check_ip_restriction, only: :tara_callback
api :GET, 'repp/v1/registrar/auth'
desc 'check user auth info and return data'
def index
registrar = current_user.registrar
render_success(data: auth_values_to_data(registrar: registrar))
end
api :POST, 'repp/v1/registrar/auth/tara_callback'
desc 'check tara callback omniauth user info and return token'
def tara_callback
user = ApiUser.from_omniauth(auth_params)
handle_non_epp_errors(user, I18n.t(:no_such_user)) and return unless user && user&.active
token = Base64.urlsafe_encode64("#{user.username}:#{user.plain_text_password}")
render_success(data: { token: token, username: user.username })
end
api :put, '/repp/v1/registrar/auth/switch_user/:new_user_id'
desc 'Switch session to another api user'
def switch_user
new_user = ApiUser.find(auth_params[:new_user_id])
unless current_user.linked_with?(new_user)
handle_non_epp_errors(new_user, 'Cannot switch to unlinked user')
return
end
@current_user = new_user
data = auth_values_to_data(registrar: current_user.registrar)
message = I18n.t('registrar.current_user.switch.switched', new_user: new_user)
token = Base64.urlsafe_encode64("#{new_user.username}:#{new_user.plain_text_password}")
render_success(data: { token: token, registrar: data }, message: message)
end
private
def auth_params
params.require(:auth).permit(:uid, :new_user_id)
end
end
end
end
end

View file

@ -19,13 +19,16 @@ module Repp
end
def update # rubocop:disable Metrics/MethodLength
authorize! :manage, :repp
affected, errored = if hostname.present?
current_user.registrar.replace_nameservers(hostname,
hostname_params[:data][:attributes],
domains: domains_from_params)
current_user.registrar
.replace_nameservers(hostname,
hostname_params[:attributes],
domains: domains_from_params)
else
current_user.registrar.add_nameservers(hostname_params[:data][:attributes],
domains: domains_from_params)
current_user.registrar
.add_nameservers(hostname_params[:attributes],
domains: domains_from_params)
end
render_success(data: data_format_for_success(affected, errored))
@ -36,34 +39,32 @@ module Repp
private
def domains_from_params
return [] unless params[:data][:domains]
return [] unless hostname_params[:domains]
params[:data][:domains].map(&:downcase)
hostname_params[:domains].map(&:downcase)
end
def data_format_for_success(affected_domains, errored_domains)
{
type: 'nameserver',
id: params[:data][:attributes][:hostname],
attributes: params[:data][:attributes],
id: hostname_params[:attributes][:hostname],
attributes: hostname_params[:attributes],
affected_domains: affected_domains,
skipped_domains: errored_domains,
}
end
def hostname_params
params.require(:data).require(%i[type])
params.require(:data).require(:attributes).require([:hostname])
params.permit(data: [
:type, :id,
{ domains: [],
attributes: [:hostname, { ipv4: [], ipv6: [] }] }
])
params.require(:data).permit(:type, :id, nameserver: [], domains: [],
attributes: [:hostname, { ipv4: [], ipv6: [] }])
.tap do |data|
data.require(:type)
data.require(:attributes).require([:hostname])
end
end
def hostname
hostname_params[:data][:id] || nil
hostname_params[:id] || nil
end
def verify_nameserver_existance

View file

@ -2,7 +2,7 @@ module Repp
module V1
module Registrar
class NotificationsController < BaseController
before_action :set_notification, only: [:update]
before_action :set_notification, only: %i[update show]
api :GET, '/repp/v1/registrar/notifications'
desc 'Get the latest unread poll message'
@ -39,7 +39,6 @@ module Repp
api :GET, '/repp/v1/registrar/notifications/:notification_id'
desc 'Get a specific poll message'
def show
@notification = current_user.registrar.notifications.find(params[:id])
data = @notification.as_json(only: %i[id text attached_obj_id attached_obj_type read])
render_success(data: data)
@ -51,6 +50,7 @@ module Repp
param :read, [true, 'true'], required: true, desc: 'Set as true to mark as read'
end
def update
authorize! :manage, :poll
# rubocop:disable Style/AndOr
handle_errors(@notification) and return unless @notification.mark_as_read
# rubocop:enable Style/AndOr

View file

@ -0,0 +1,111 @@
module Repp
module V1
module Registrar
class SummaryController < BaseController
api :GET, 'repp/v1/registrar/summary'
desc 'check user summary info and return data'
def index
user = current_user
registrar = user.registrar
if can?(:manage, :poll)
user_notifications = user.unread_notifications
notification = user_notifications.order('created_at DESC').take
notifications_count = user_notifications.count
if notification&.attached_obj_type && notification&.attached_obj_id
begin
object = object_by_type(notification.attached_obj_type)
.find(notification.attached_obj_id)
rescue => e
# the data model might be inconsistent; or ...
# this could happen if the registrar does not dequeue messages, and then the domain was deleted
# SELECT messages.id, domains.name, messages.body FROM messages LEFT OUTER
# JOIN domains ON attached_obj_id::INTEGER = domains.id
# WHERE attached_obj_type = 'Epp::Domain' AND name IS NULL;
message = 'orphan message, domain deleted, registrar should dequeue: '
Rails.logger.error message + e.to_s
end
end
end
data = serialize_data(registrar: registrar,
notification: notification,
notifications_count: notifications_count,
object: object)
render_success(data: data)
end
private
def object_by_type(object_type)
Object.const_get(object_type)
rescue NameError
Object.const_get("Version::#{object_type}")
end
# rubocop:disable Metrics/MethodLength
def serialize_data(registrar:, notification:, notifications_count:, object: nil)
data = current_user.as_json(only: %i[id username])
data[:registrar_name] = registrar.name
data[:registrar_reg_no] = registrar.reg_no
data[:last_login_date] = last_login_date
data[:domains] = registrar.domains.count if can? :view, Depp::Domain
data[:contacts] = registrar.contacts.count if can? :view, Depp::Contact
data[:phone] = registrar.phone
data[:email] = registrar.email
data[:billing_email] = registrar.billing_email
data[:billing_address] = registrar.address
data[:notification] = serialized_notification(notification, object)
data[:notifications_count] = notifications_count
data
end
# rubocop:enable Metrics/MethodLength
def last_login_date
q = ApiLog::ReppLog.ransack({ request_path_eq: '/repp/v1/registrar/auth',
response_code_eq: '200',
api_user_name_cont: current_user.username,
request_method_eq: 'GET' })
q.sorts = 'id desc'
q.result.offset(1).first&.created_at
end
def serialized_notification(notification, object)
return unless notification
notification.created_at = notification.created_at.utc.xmlschema
obj_data = serialized_object(object, notification.attached_obj_type)
notification.as_json(only: %i[id text created_at attached_obj_id attached_obj_type])
.merge({ attached_obj_data: obj_data })
end
def serialized_object(object, obj_type)
return unless object
case obj_type
when 'DomainTransfer'
{
name: object.domain_name,
trStatus: object.status,
reID: object.new_registrar.code,
reDate: object.transfer_requested_at.try(:iso8601),
acID: object.old_registrar.code,
acDate: object.transferred_at.try(:iso8601) || object.wait_until.try(:iso8601),
exDate: object.domain_valid_to.iso8601,
}
when 'ContactUpdateAction'
{
contacts: object.to_non_available_contact_codes,
operation: object.operation,
opDate: object.created_at.utc.xmlschema,
svTrid: object.id,
who: object.user.username,
reason: 'Auto-update according to official data',
}
end
end
end
end
end
end