Add contacts, keyrelays, poll

This commit is contained in:
Martin Lensment 2015-01-16 15:30:31 +02:00
parent ea51bb50b2
commit d468068b96
5 changed files with 469 additions and 7 deletions

View file

@ -1,9 +1,197 @@
class Epp::ContactsController < ApplicationController class Epp::ContactsController < ApplicationController
protect_from_forgery with: :null_session include Epp::Common
include Shared::UserStamper ## Refactor this?
helper WhodunnitHelper ## Refactor this?
def user_for_paper_trail ## Refactor this?
current_epp_user ? "#{current_epp_user.id}-EppUser" : nil
end
def create def create
@contact = Contact.new(contact_and_address_attributes)
@contact.registrar = current_epp_user.registrar
render_epp_response '/epp/contacts/create' and return if stamp(@contact) && @contact.save
handle_errors(@contact)
end
def update
# FIXME: Update returns 2303 update multiple times
code = params_hash['epp']['command']['update']['update'][:id]
@contact = Contact.where(code: code).first
# if update_rights? && stamp(@contact) && @contact.update_attributes(contact_and_address_attributes(:update))
if owner? && stamp(@contact) && @contact.update_attributes(contact_and_address_attributes(:update))
render_epp_response 'epp/contacts/update'
else
contact_exists?(code)
handle_errors(@contact) and return
end
end
# rubocop:disable Metrics/CyclomaticComplexity
def delete
@contact = find_contact
handle_errors(@contact) and return unless rights? # owner?
handle_errors(@contact) and return unless @contact
handle_errors(@contact) and return unless @contact.destroy_and_clean
render_epp_response '/epp/contacts/delete'
end
# rubocop:enable Metrics/CyclomaticComplexity
def check
ph = params_hash['epp']['command']['check']['check']
@contacts = Contact.check_availability(ph[:id])
render_epp_response '/epp/contacts/check'
end end
def info def info
handle_errors(@contact) and return unless @contact && rights?
# handle_errors(@contact) and return unless rights?
@disclosure = ContactDisclosure.default_values.merge(@contact.disclosure.try(:as_hash) || {})
@disclosure_policy = @contact.disclosure.try(:attributes_with_flag)
@owner = owner?(false)
# need to reload contact eagerly
@contact = find_contact('with eager load') if @owner # for clarity, could just be true
render_epp_response 'epp/contacts/info'
end
def renew
epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') }
handle_errors
end
## HELPER METHODS
private
## CREATE
def validate_create
@ph = params_hash['epp']['command']['create']['create']
return false unless validate_params
xml_attrs_present?(@ph, [%w(postalInfo name), %w(postalInfo addr city), %w(postalInfo addr cc),
%w(ident), %w(voice), %w(email)])
epp_errors.empty?
end
## UPDATE
def validate_update
@ph = params_hash['epp']['command']['update']['update']
update_attrs_present?
# xml_attrs_present?(@ph, [['id'], %w(authInfo pw)])
xml_attrs_present?(@ph, [['id']])
end
def contact_exists?(code)
return true if @contact.is_a?(Contact)
epp_errors << { code: '2303', msg: t('errors.messages.epp_obj_does_not_exist'),
value: { obj: 'id', val: code } }
end
def update_attrs_present?
return true if parsed_frame.css('add').present?
return true if parsed_frame.css('rem').present?
return true if parsed_frame.css('chg').present?
epp_errors << { code: '2003', msg: I18n.t('errors.messages.required_parameter_missing', key: 'add, rem or chg') }
end
## DELETE
def validate_delete
@ph = params_hash['epp']['command']['delete']['delete']
xml_attrs_present?(@ph, [['id']])
end
## check
def validate_check
@ph = params_hash['epp']['command']['check']['check']
xml_attrs_present?(@ph, [['id']])
end
## info
def validate_info # and process
@ph = params_hash['epp']['command']['info']['info']
return false unless xml_attrs_present?(@ph, [['id']])
@contact = find_contact
return false unless @contact
return true if current_epp_user.registrar == @contact.registrar || xml_attrs_present?(@ph, [%w(authInfo pw)])
false
end
## SHARED
def find_contact(eager_load = nil)
if eager_load
contact = Contact.includes(address: :country).find_by(code: @ph[:id])
else
contact = Contact.find_by(code: @ph[:id])
end
unless contact
epp_errors << { code: '2303',
msg: t('errors.messages.epp_obj_does_not_exist'),
value: { obj: 'id', val: @ph[:id] } }
end
contact
end
def owner?(with_errors = true)
return false unless find_contact
return true if @contact.registrar == current_epp_user.registrar
return false unless with_errors
epp_errors << { code: '2201', msg: t('errors.messages.epp_authorization_error') }
false
end
def rights?
pw = @ph.try(:[], :authInfo).try(:[], :pw)
return true if current_epp_user.try(:registrar) == @contact.try(:registrar)
return true if pw && @contact.auth_info_matches(pw) # @contact.try(:auth_info_matches, pw)
epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') }
false
end
def update_rights?
pw = @ph.try(:[], :authInfo).try(:[], :pw)
return true if pw && @contact.auth_info_matches(pw)
epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') }
false
end
def contact_and_address_attributes(type = :create)
case type
when :update
# TODO: support for rem/add
contact_hash = merge_attribute_hash(@ph[:chg], type).delete_if { |_k, v| v.empty? }
else
contact_hash = merge_attribute_hash(@ph, type)
end
contact_hash[:ident_type] = ident_type unless ident_type.nil?
contact_hash
end
def merge_attribute_hash(prms, type)
contact_hash = Contact.extract_attributes(prms, type)
contact_hash = contact_hash.merge(
Address.extract_attributes((prms.try(:[], :postalInfo) || []))
)
contact_hash[:disclosure_attributes] =
ContactDisclosure.extract_attributes(parsed_frame)
contact_hash
end
def ident_type
result = parsed_frame.css('ident').first.try(:attributes).try(:[], 'type').try(:value)
return nil unless result
Contact::IDENT_TYPES.any? { |type| return type if result.include?(type) }
nil
end
def validate_params
return true if @ph
epp_errors << { code: '2001', msg: t(:'errors.messages.epp_command_syntax_error') }
false
end end
end end

View file

@ -2,6 +2,16 @@ class Epp::DomainsController < ApplicationController
include Epp::Common include Epp::Common
def create def create
@domain = Epp::EppDomain.new(domain_create_params)
@domain.parse_and_attach_domain_dependencies(params[:parsed_frame])
@domain.parse_and_attach_ds_data(params[:parsed_frame].css('extension create'))
if @domain.errors.any? || !@domain.save
handle_errors(@domain)
else
render_epp_response '/epp/domains/create'
end
end end
def info def info
@ -16,12 +26,178 @@ class Epp::DomainsController < ApplicationController
render_epp_response '/epp/domains/check' render_epp_response '/epp/domains/check'
end end
def renew
# TODO: support period unit
@domain = find_domain
handle_errors(@domain) and return unless @domain
handle_errors(@domain) and return unless @domain.renew(
params[:parsed_frame].css('curExpDate').text,
params[:parsed_frame].css('period').text,
params[:parsed_frame].css('period').first['unit']
)
render_epp_response '/epp/domains/renew'
end
# rubocop:disable Metrics/CyclomaticComplexity
def update
@domain = find_domain
handle_errors(@domain) and return unless @domain
@domain.parse_and_detach_domain_dependencies(params[:parsed_frame].css('rem'))
@domain.parse_and_detach_ds_data(params[:parsed_frame].css('extension rem'))
@domain.parse_and_attach_domain_dependencies(params[:parsed_frame].css('add'))
@domain.parse_and_attach_ds_data(params[:parsed_frame].css('extension add'))
@domain.parse_and_update_domain_dependencies(params[:parsed_frame].css('chg'))
@domain.attach_legal_document(Epp::EppDomain.parse_legal_document_from_frame(params[:parsed_frame]))
if @domain.errors.any? || !@domain.save
handle_errors(@domain)
else
render_epp_response '/epp/domains/success'
end
end
# rubocop: disable Metrics/PerceivedComplexity
# rubocop: disable Metrics/MethodLength
def transfer
@domain = find_domain(secure: false)
handle_errors(@domain) and return unless @domain
handle_errors(@domain) and return unless @domain.authenticate(domain_transfer_params[:pw])
if domain_transfer_params[:action] == 'query'
if @domain.pending_transfer
@domain_transfer = @domain.pending_transfer
else
@domain_transfer = @domain.query_transfer(domain_transfer_params, params[:parsed_frame])
handle_errors(@domain) and return unless @domain_transfer
end
elsif domain_transfer_params[:action] == 'approve'
if @domain.pending_transfer
@domain_transfer = @domain.approve_transfer(domain_transfer_params, params[:parsed_frame])
handle_errors(@domain) and return unless @domain_transfer
else
epp_errors << { code: '2303', msg: I18n.t('pending_transfer_was_not_found') }
handle_errors(@domain) and return
end
elsif domain_transfer_params[:action] == 'reject'
if @domain.pending_transfer
@domain_transfer = @domain.reject_transfer(domain_transfer_params, params[:parsed_frame])
handle_errors(@domain) and return unless @domain_transfer
else
epp_errors << { code: '2303', msg: I18n.t('pending_transfer_was_not_found') }
handle_errors(@domain) and return
end
end
render_epp_response '/epp/domains/transfer'
end
# rubocop: enable Metrics/MethodLength
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def delete
@domain = find_domain
handle_errors(@domain) and return unless @domain
handle_errors(@domain) and return unless @domain.can_be_deleted?
@domain.attach_legal_document(Epp::EppDomain.parse_legal_document_from_frame(params[:parsed_frame]))
@domain.save(validate: false)
handle_errors(@domain) and return unless @domain.destroy
render_epp_response '/epp/domains/success'
end
# rubocop:enbale Metrics/CyclomaticComplexity
private private
def validate_info
@ph = params_hash['epp']['command']['info']['info']
xml_attrs_present?(@ph, [['name']])
end
def validate_check def validate_check
epp_request_valid?('name') epp_request_valid?('name')
end end
def validate_create
ret = true
# TODO: Verify contact presence if registrant is juridical
attrs_present = epp_request_valid?('name', 'ns', 'registrant', 'legalDocument')
ret = false unless attrs_present
if params[:parsed_frame].css('hostObj').any?
epp_errors << { code: '2306', msg: I18n.t('host_obj_is_not_allowed') }
ret = false
end
if params[:parsed_frame].css('dsData').count > 0 && params[:parsed_frame].css('create > keyData').count > 0
epp_errors << { code: '2306', msg: I18n.t('ds_data_and_key_data_must_not_exists_together') }
ret = false
end
ret
end
def validate_renew
@ph = params_hash['epp']['command']['renew']['renew']
xml_attrs_present?(@ph, [['name'], ['curExpDate'], ['period']])
end
def validate_update
@ph = params_hash['epp']['command']['update']['update']
if params[:parsed_frame].css('chg registrant').present? && params[:parsed_frame].css('legalDocument').blank?
xml_attrs_present?(@ph, [['name'], ['legalDocument']])
else
xml_attrs_present?(@ph, [['name']])
end
end
## TRANSFER
def validate_transfer
@ph = params_hash['epp']['command']['transfer']['transfer']
attrs_present = xml_attrs_present?(@ph, [['name']])
return false unless attrs_present
op = params[:parsed_frame].css('transfer').first[:op]
return true if %w(approve query reject).include?(op)
epp_errors << { code: '2306', msg: I18n.t('errors.messages.attribute_op_is_invalid') }
false
end
## DELETE
def validate_delete
epp_request_valid?('name', 'legalDocument')
end
def domain_create_params
name = params[:parsed_frame].css('name').text
period = params[:parsed_frame].css('period').text
{
name: name,
registrar_id: current_epp_user.registrar.try(:id),
registered_at: Time.now,
period: (period.to_i == 0) ? 1 : period.to_i,
period_unit: Epp::EppDomain.parse_period_unit_from_frame(params[:parsed_frame]) || 'y'
}
end
def domain_transfer_params
res = {}
res[:pw] = parsed_frame.css('pw').first.try(:text)
res[:action] = parsed_frame.css('transfer').first[:op]
res[:current_user] = current_epp_user
res
end
def find_domain(secure = { secure: true }) def find_domain(secure = { secure: true })
domain_name = params[:parsed_frame].css('name').text.strip.downcase domain_name = params[:parsed_frame].css('name').text.strip.downcase
domain = Epp::EppDomain.find_by(name: domain_name) domain = Epp::EppDomain.find_by(name: domain_name)

View file

@ -0,0 +1,51 @@
class Epp::KeyrelaysController < ApplicationController
include Epp::Common
# rubocop: disable Metrics/PerceivedComplexity
# rubocop: disable Metrics/CyclomaticComplexity
def keyrelay
@domain = find_domain
handle_errors(@domain) and return unless @domain
handle_errors(@domain) and return unless @domain.authenticate(parsed_frame.css('pw').text)
handle_errors(@domain) and return unless @domain.keyrelay(parsed_frame, current_epp_user.registrar)
render_epp_response '/epp/shared/success'
end
private
def validate_keyrelay
epp_request_valid?('pubKey', 'flags', 'protocol', 'alg', 'name', 'pw')
begin
abs_datetime = parsed_frame.css('absolute').text
abs_datetime = DateTime.parse(abs_datetime) if abs_datetime.present?
rescue => _e
epp_errors << {
code: '2005',
msg: I18n.t('unknown_expiry_absolute_pattern'),
value: { obj: 'expiry_absolute', val: abs_datetime }
}
end
epp_errors.empty?
end
# rubocop: enable Metrics/PerceivedComplexity
# rubocop: enable Metrics/CyclomaticComplexity
def find_domain
domain_name = parsed_frame.css('name').text.strip.downcase
domain = Epp::EppDomain.find_by(name: domain_name)
unless domain
epp_errors << {
code: '2303',
msg: I18n.t('errors.messages.epp_domain_not_found'),
value: { obj: 'name', val: domain_name }
}
return nil
end
domain
end
end

View file

@ -0,0 +1,48 @@
class Epp::PollsController < ApplicationController
include Epp::Common
def poll
req_poll if parsed_frame.css('poll').first['op'] == 'req'
ack_poll if parsed_frame.css('poll').first['op'] == 'ack'
end
def req_poll
@message = current_epp_user.queued_messages.last
render_epp_response 'epp/poll/poll_no_messages' and return unless @message
if @message.attached_obj_type && @message.attached_obj_id
@object = Object.const_get(@message.attached_obj_type).find(@message.attached_obj_id)
end
if @message.attached_obj_type == 'Keyrelay'
render_epp_response 'epp/poll/poll_keyrelay'
else
render_epp_response 'epp/poll/poll_req'
end
end
def ack_poll
@message = current_epp_user.queued_messages.find_by(id: parsed_frame.css('poll').first['msgID'])
unless @message
epp_errors << {
code: '2303',
msg: I18n.t('message_was_not_found'),
value: { obj: 'msgID', val: parsed_frame.css('poll').first['msgID'] }
}
handle_errors and return
end
handle_errors(@message) and return unless @message.dequeue
render_epp_response 'epp/poll/poll_ack'
end
private
def validate_poll
op = parsed_frame.css('poll').first[:op]
return true if %w(ack req).include?(op)
epp_errors << { code: '2306', msg: I18n.t('errors.messages.attribute_op_is_invalid') }
false
end
end

View file

@ -20,12 +20,11 @@ class EppConstraint
end end
Rails.application.routes.draw do Rails.application.routes.draw do
namespace(:epp) do namespace(:epp, defaults: { format: :xml }) do
post 'command/info', to: 'domains#info', defaults: { format: :xml }, constraints: EppConstraint.new(:domain) post 'command/:action', controller: 'domains', constraints: EppConstraint.new(:domain)
post 'command/info', to: 'contacts#info', defaults: { format: :xml }, constraints: EppConstraint.new(:contact) post 'command/:action', controller: 'contacts', constraints: EppConstraint.new(:contact)
post 'command/poll', to: 'polls#poll'
post 'command/check', to: 'domains#check', defaults: { format: :xml }, constraints: EppConstraint.new(:domain) post 'command/keyrelay', to: 'keyrelays#keyrelay'
post 'command/check', to: 'contacts#check', defaults: { format: :xml }, constraints: EppConstraint.new(:contact)
match 'session/:command', to: 'sessions#proxy', defaults: { format: :xml }, via: [:get, :post] match 'session/:command', to: 'sessions#proxy', defaults: { format: :xml }, via: [:get, :post]
# match 'command/:command', to: 'commands#proxy', defaults: { format: :xml }, via: [:post, :get] # match 'command/:command', to: 'commands#proxy', defaults: { format: :xml }, via: [:post, :get]