Merge pull request #620 from internetee/revert-551d1799d1782a9dbf5edfb57a0fa4c5fe6bef83

Revert "Revert "Registry 569""
This commit is contained in:
Timo Võhmar 2017-11-15 11:27:04 +02:00 committed by GitHub
commit ca13c08ea8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 2462 additions and 315 deletions

View file

@ -27,6 +27,7 @@ engines:
- HACK - HACK
rubocop: rubocop:
enabled: true enabled: true
channel: rubocop-0-49
reek: reek:
enabled: true enabled: true
checks: checks:

1168
.reek

File diff suppressed because it is too large Load diff

View file

@ -104,6 +104,7 @@ gem 'activerecord-import', '0.7.0' # for inserting dummy data
# for generating pdf # for generating pdf
gem 'pdfkit', '0.6.2' gem 'pdfkit', '0.6.2'
gem 'jquery-ui-rails', '5.0.5' gem 'jquery-ui-rails', '5.0.5'
gem 'active_model-errors_details' # Backport from Rails 5, https://github.com/rails/rails/pull/18322
group :development do group :development do
gem 'spring' gem 'spring'

View file

@ -79,6 +79,9 @@ GEM
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
active_model-errors_details (1.3.1)
activemodel (>= 3.2.13, < 5.0.0)
activesupport
activejob (4.2.7.1) activejob (4.2.7.1)
activesupport (= 4.2.7.1) activesupport (= 4.2.7.1)
globalid (>= 0.3.0) globalid (>= 0.3.0)
@ -489,6 +492,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
SyslogLogger (= 2.0) SyslogLogger (= 2.0)
active_model-errors_details
activerecord-import (= 0.7.0) activerecord-import (= 0.7.0)
airbrake airbrake
autodoc autodoc

View file

@ -2,6 +2,7 @@ module Admin
class ContactsController < BaseController class ContactsController < BaseController
load_and_authorize_resource load_and_authorize_resource
before_action :set_contact, only: [:show] before_action :set_contact, only: [:show]
helper_method :ident_types
def index def index
params[:q] ||= {} params[:q] ||= {}
@ -79,5 +80,9 @@ module Admin
params[:q][:created_at_lteq] = ca_cache params[:q][:created_at_lteq] = ca_cache
end end
def ident_types
Contact::Ident.types
end
end end
end end

View file

@ -152,9 +152,6 @@ class EppController < ApplicationController
code: '1', code: '1',
msg: 'handle_errors was executed when there were actually no errors' msg: 'handle_errors was executed when there were actually no errors'
} }
# rubocop:disable Rails/Output
puts "FULL MESSAGE: #{obj.errors.full_messages} #{obj.errors.inspect}" if Rails.env.test?
# rubocop: enable Rails/Output
end end
@errors.uniq! @errors.uniq!

View file

@ -2,6 +2,7 @@ class Registrar
class ContactsController < DeppController class ContactsController < DeppController
before_action :init_epp_contact before_action :init_epp_contact
helper_method :address_processing? helper_method :address_processing?
helper_method :ident_types
def index def index
authorize! :view, Depp::Contact authorize! :view, Depp::Contact
@ -140,5 +141,9 @@ class Registrar
def address_processing? def address_processing?
Contact.address_processing? Contact.address_processing?
end end
def ident_types
Contact::Ident.types
end
end end
end end

View file

@ -11,6 +11,12 @@ module EppErrors
epp_errors << collect_child_errors(attr) epp_errors << collect_child_errors(attr)
end end
if self.class.reflect_on_aggregation(attr)
aggregation = send(attr)
epp_errors << collect_aggregation_errors(aggregation)
next
end
epp_errors << collect_parent_errors(attr, errors) epp_errors << collect_parent_errors(attr, errors)
end end
@ -46,6 +52,31 @@ module EppErrors
epp_errors epp_errors
end end
def collect_aggregation_errors(aggregation)
epp_errors = []
aggregation.errors.details.each do |attr, error_details|
error_details.each do |error_detail|
aggregation.class.epp_code_map.each do |epp_code, attr_to_error|
epp_code_found = attr_to_error.any? { |i| i == [attr, error_detail[:error]] }
next unless epp_code_found
message = aggregation.errors.generate_message(attr, error_detail[:error], error_detail)
message = aggregation.errors.full_message(attr, message)
if attr != :base
message = "#{aggregation.model_name.human} #{message.camelize(:lower)}"
end
epp_errors << { code: epp_code, msg: message }
end
end
end
epp_errors
end
def find_epp_code_and_value(msg) def find_epp_code_and_value(msg)
epp_code_map.each do |code, values| epp_code_map.each do |code, values|
values.each do |x| values.each do |x|

View file

@ -19,27 +19,22 @@ class Contact < ActiveRecord::Base
accepts_nested_attributes_for :legal_documents accepts_nested_attributes_for :legal_documents
validates :name, :phone, :email, :ident, :ident_type, presence: true validates :name, :email, presence: true
validates :street, :city, :zip, :country_code, presence: true, if: 'self.class.address_processing?' validates :street, :city, :zip, :country_code, presence: true, if: 'self.class.address_processing?'
validates :phone, format: /\+[0-9]{1,3}\.[0-9]{1,14}?/, phone: true validates :phone, presence: true, e164: true, phone: true
validates :email, format: /@/ validates :email, format: /@/
validates :email, email_format: { message: :invalid }, if: proc { |c| c.email_changed? } validates :email, email_format: { message: :invalid }, if: proc { |c| c.email_changed? }
validates :ident,
format: { with: /\d{4}-\d{2}-\d{2}/, message: :invalid_birthday_format },
if: proc { |c| c.ident_type == 'birthday' }
validates :ident_country_code, presence: true, if: proc { |c| %w(org priv).include? c.ident_type }, on: :create
validates :code, validates :code,
uniqueness: { message: :epp_id_taken }, uniqueness: { message: :epp_id_taken },
format: { with: /\A[\w\-\:\.\_]*\z/i, message: :invalid }, format: { with: /\A[\w\-\:\.\_]*\z/i, message: :invalid },
length: { maximum: 100, message: :too_long_contact_code } length: { maximum: 100, message: :too_long_contact_code }
validates_associated :identifier
validate :val_ident_type
validate :val_ident_valid_format?
validate :validate_html validate :validate_html
validate :validate_country_code validate :validate_country_code
validate :validate_ident_country_code
after_initialize do after_initialize do
self.status_notes = {} if status_notes.nil? self.status_notes = {} if status_notes.nil?
@ -49,8 +44,15 @@ class Contact < ActiveRecord::Base
before_validation :to_upcase_country_code before_validation :to_upcase_country_code
before_validation :strip_email before_validation :strip_email
before_create :generate_auth_info before_create :generate_auth_info
before_update :manage_emails before_update :manage_emails
composed_of :identifier,
class_name: 'Contact::Ident',
constructor: proc { |code, type, country_code| Contact::Ident.new(code: code,
type: type,
country_code: country_code) },
mapping: [%w[ident code], %w[ident_type type], %w[ident_country_code country_code]]
def manage_emails def manage_emails
return nil unless email_changed? return nil unless email_changed?
return nil unless deliver_emails == true return nil unless deliver_emails == true
@ -76,12 +78,6 @@ class Contact < ActiveRecord::Base
BIRTHDAY = 'birthday'.freeze BIRTHDAY = 'birthday'.freeze
PASSPORT = 'passport' PASSPORT = 'passport'
IDENT_TYPES = [
ORG, # Company registry code (or similar)
PRIV, # National idendtification number
BIRTHDAY # Birthday date
]
attr_accessor :deliver_emails attr_accessor :deliver_emails
attr_accessor :domain_transfer # hack but solves problem faster attr_accessor :domain_transfer # hack but solves problem faster
@ -219,10 +215,6 @@ class Contact < ActiveRecord::Base
STDOUT << "#{Time.zone.now.utc} - Successfully destroyed #{counter} orphaned contacts\n" unless Rails.env.test? STDOUT << "#{Time.zone.now.utc} - Successfully destroyed #{counter} orphaned contacts\n" unless Rails.env.test?
end end
def privs
where("ident_type = '#{PRIV}'")
end
def admin_statuses def admin_statuses
[ [
SERVER_UPDATE_PROHIBITED, SERVER_UPDATE_PROHIBITED,
@ -299,28 +291,6 @@ class Contact < ActiveRecord::Base
name || '[no name]' name || '[no name]'
end end
def val_ident_type
errors.add(:ident_type, :epp_ident_type_invalid, code: code) if !%w(org priv birthday).include?(ident_type)
end
def val_ident_valid_format?
case ident_country_code
when 'EE'.freeze
err_msg = "invalid_EE_identity_format#{"_update" if id}".to_sym
case ident_type
when 'priv'.freeze
errors.add(:ident, err_msg) unless Isikukood.new(ident).valid?
when 'org'.freeze
# !%w(1 7 8 9).freeze.include?(ident.first) ||
if ident.size != 8 || !(ident =~/\A[0-9]{8}\z/)
errors.add(:ident, err_msg)
end
when BIRTHDAY
errors.add(:ident, err_msg) if id.blank? # only for create action right now. Later for all of them
end
end
end
def validate_html def validate_html
self.class.columns.each do |column| self.class.columns.each do |column|
next unless column.type == :string next unless column.type == :string
@ -334,7 +304,6 @@ class Contact < ActiveRecord::Base
end end
end end
def org? def org?
ident_type == ORG ident_type == ORG
end end
@ -344,10 +313,6 @@ class Contact < ActiveRecord::Base
!org? !org?
end end
def birthday?
ident_type == BIRTHDAY
end
def generate_auth_info def generate_auth_info
return if @generate_auth_info_disabled return if @generate_auth_info_disabled
return if auth_info.present? return if auth_info.present?
@ -424,10 +389,6 @@ class Contact < ActiveRecord::Base
errors.add(:country_code, :invalid) unless Country.new(country_code) errors.add(:country_code, :invalid) unless Country.new(country_code)
end end
def validate_ident_country_code
errors.add(:ident, :invalid_country_code) unless Country.new(ident_country_code)
end
def related_domain_descriptions def related_domain_descriptions
ActiveSupport::Deprecation.warn('Use #domain_names_with_roles') ActiveSupport::Deprecation.warn('Use #domain_names_with_roles')
@ -572,7 +533,7 @@ class Contact < ActiveRecord::Base
return if changes.slice(*(self.class.column_names - ["updated_at", "created_at", "statuses", "status_notes"])).empty? return if changes.slice(*(self.class.column_names - ["updated_at", "created_at", "statuses", "status_notes"])).empty?
names = related_domain_descriptions.keys names = related_domain_descriptions.keys
UpdateWhoisRecordJob.enqueue(names, :domain) if names.present? UpdateWhoisRecordJob.enqueue(names, 'domain') if names.present?
end end
def children_log def children_log

View file

@ -0,0 +1,74 @@
class Contact::Ident
include ActiveModel::Model
attr_accessor :code
attr_accessor :type
attr_accessor :country_code
validates :code, presence: true
validates :code, national_id: true, if: :national_id?
validates :code, reg_no: true, if: :reg_no?
validates :code, iso8601: { date_only: true }, if: :birthday?
validates :type, presence: true, inclusion: { in: proc { types } }
validates :country_code, presence: true, iso31661_alpha2: true
validates_with MismatchValidator
def self.epp_code_map
{
'2003' => [
[:code, :blank],
[:type, :blank],
[:country_code, :blank]
],
'2005' => [
[:base, :mismatch],
[:code, :invalid_national_id],
[:code, :invalid_reg_no],
[:code, :invalid_iso8601_date],
[:country_code, :invalid_iso31661_alpha2]
]
}
end
def self.types
%w[org priv birthday]
end
def marked_for_destruction?
false
end
def birthday?
type == 'birthday'
end
def national_id?
type == 'priv'
end
def reg_no?
type == 'org'
end
def country
Country.new(country_code)
end
def ==(other_ident)
if other_ident.is_a?(self.class)
(code == other_ident.code) &&
(type == other_ident.type) &&
(country_code == other_ident.country_code)
else
false
end
end
private
# https://github.com/rails/rails/issues/1513
def validation_context=(_value)
;
end
end

View file

@ -109,7 +109,7 @@ class Epp::Contact < Contact
end end
delegate :ident_attr_valid?, to: :class delegate :ident_attr_valid?, to: :class
def epp_code_map # rubocop:disable Metrics/MethodLength def epp_code_map
{ {
'2003' => [ # Required parameter missing '2003' => [ # Required parameter missing
[:name, :blank], [:name, :blank],
@ -124,31 +124,19 @@ class Epp::Contact < Contact
[:name, :invalid], [:name, :invalid],
[:phone, :invalid], [:phone, :invalid],
[:email, :invalid], [:email, :invalid],
[:ident, :invalid],
[:ident, :invalid_EE_identity_format],
[:ident, :invalid_EE_identity_format_update],
[:ident, :invalid_birthday_format],
[:ident, :invalid_country_code],
[:country_code, :invalid], [:country_code, :invalid],
[:ident_type, :missing],
[:code, :invalid], [:code, :invalid],
[:code, :too_long_contact_code] [:code, :too_long_contact_code]
], ],
'2302' => [ # Object exists '2302' => [ # Object exists
[:code, :epp_id_taken] [:code, :epp_id_taken]
], ],
'2304' => [ # Object status prohibits operation
[:ident_type, :epp_ident_type_invalid, { value: { obj: 'code', val: code}, interpolation: {code: code}}]
],
'2305' => [ # Association exists '2305' => [ # Association exists
[:domains, :exist] [:domains, :exist]
],
'2306' => [ # Parameter policy error
] ]
} }
end end
# rubocop:disable Metrics/AbcSize
def update_attributes(frame, current_user) def update_attributes(frame, current_user)
return super if frame.blank? return super if frame.blank?
at = {}.with_indifferent_access at = {}.with_indifferent_access
@ -158,9 +146,6 @@ class Epp::Contact < Contact
at[:statuses] = statuses - statuses_attrs(frame.css('rem'), 'rem') + statuses_attrs(frame.css('add'), 'add') at[:statuses] = statuses - statuses_attrs(frame.css('rem'), 'rem') + statuses_attrs(frame.css('add'), 'add')
end end
# legal_frame = frame.css('legalDocument').first
# at[:legal_documents_attributes] = self.class.legal_document_attrs(legal_frame)
if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame)) if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame))
frame.css("legalDocument").first.content = doc.path if doc && doc.persisted? frame.css("legalDocument").first.content = doc.path if doc && doc.persisted?
self.legal_document_id = doc.id self.legal_document_id = doc.id
@ -168,29 +153,28 @@ class Epp::Contact < Contact
self.deliver_emails = true # turn on email delivery for epp self.deliver_emails = true # turn on email delivery for epp
ident_frame = frame.css('ident').first
# allow to update ident code for legacy contacts # https://github.com/internetee/registry/issues/576
if frame.css('ident').first if ident_frame
self.ident_updated_at ||= Time.zone.now # not in use if identifier.valid?
ident_frame = frame.css('ident').first submitted_ident = Ident.new(code: ident_frame.text,
type: ident_frame.attr('type'),
country_code: ident_frame.attr('cc'))
if ident_frame && ident_attr_valid?(ident_frame) report_valid_ident_error if submitted_ident != identifier
org_priv = %w(org priv).freeze
if ident_country_code.blank? && org_priv.include?(ident_type) && org_priv.include?(ident_frame.attr('type'))
at.merge!(ident_country_code: ident_frame.attr('cc'), ident_type: ident_frame.attr('type'))
elsif ident_type == "birthday" && !ident[/\A\d{4}-\d{2}-\d{2}\z/] && (Date.parse(ident) rescue false)
at.merge!(ident: ident_frame.text)
at.merge!(ident_country_code: ident_frame.attr('cc')) if ident_frame.attr('cc').present?
elsif ident_type == "birthday" && ident_country_code.blank?
at.merge!(ident_country_code: ident_frame.attr('cc'))
elsif ident_type.blank? && ident_country_code.blank?
at.merge!(ident_type: ident_frame.attr('type'))
at.merge!(ident_country_code: ident_frame.attr('cc')) if ident_frame.attr('cc').present?
else
throw :epp_error, {code: '2306', msg: I18n.t(:ident_update_error)}
end
else else
throw :epp_error, {code: '2306', msg: I18n.t(:ident_update_error)} ident_update_attempt = ident_frame.text.present? && (ident_frame.text != ident)
report_ident_update_error if ident_update_attempt
identifier = Ident.new(code: ident,
type: ident_frame.attr('type'),
country_code: ident_frame.attr('cc'))
identifier.validate
self.identifier = identifier
self.ident_updated_at ||= Time.zone.now
end end
end end
@ -199,7 +183,6 @@ class Epp::Contact < Contact
super(at) super(at)
end end
# rubocop:enable Metrics/AbcSize
def statuses_attrs(frame, action) def statuses_attrs(frame, action)
status_list = status_list_from(frame) status_list = status_list_from(frame)
@ -259,4 +242,13 @@ class Epp::Contact < Contact
self.legal_document_id = doc.id self.legal_document_id = doc.id
end end
private
def report_valid_ident_error
throw :epp_error, { code: '2308', msg: I18n.t('epp.contacts.errors.valid_ident') }
end
def report_ident_update_error
throw :epp_error, { code: '2308', msg: I18n.t('epp.contacts.errors.ident_update') }
end
end end

View file

@ -0,0 +1,22 @@
module EPP
class Response
attr_accessor :results
def self.from_xml(xml)
xml_doc = Nokogiri::XML(xml)
response = new
result_elements = xml_doc.css('result')
result_elements.each do |result_element|
response.results << Result.new(result_element[:code].to_s, result_element.text.strip)
end
response
end
def initialize
@results = []
end
end
end

View file

@ -0,0 +1,28 @@
module EPP
class Response
class Result
CODE_TO_TYPE = {
'1000' => :success,
'1001' => :success_pending,
'1300' => :success_empty_queue,
'1301' => :success_dequeue,
'2001' => :syntax_error,
'2003' => :required_param_missing,
'2005' => :param_syntax_error,
'2308' => :data_management_policy_violation
}
attr_accessor :code
attr_accessor :message
def initialize(code, message)
@code = code
@message = message
end
def self.codes
CODE_TO_TYPE
end
end
end
end

View file

@ -9,7 +9,6 @@ class Registrar < ActiveRecord::Base
has_many :accounts has_many :accounts
has_many :nameservers, through: :domains has_many :nameservers, through: :domains
has_many :whois_records has_many :whois_records
has_many :priv_contacts, -> { privs }, class_name: 'Contact'
has_many :white_ips, dependent: :destroy has_many :white_ips, dependent: :destroy
delegate :balance, to: :cash_account, allow_nil: true delegate :balance, to: :cash_account, allow_nil: true

View file

@ -0,0 +1,20 @@
class Contact::Ident::MismatchValidator < ActiveModel::Validator
Mismatch = Struct.new(:type, :country)
def self.mismatches
[
Mismatch.new('birthday', Country.new('EE')),
]
end
def validate(record)
record.errors.add(:base, :mismatch, type: record.type, country: record.country) if mismatched?(record)
end
private
def mismatched?(record)
mismatch = Mismatch.new(record.type, record.country)
self.class.mismatches.include?(mismatch)
end
end

View file

@ -0,0 +1,22 @@
class Contact::Ident::NationalIdValidator < ActiveModel::EachValidator
def self.country_specific_validations
{
Country.new('EE') => proc { |code| Isikukood.new(code).valid? },
}
end
def validate_each(record, attribute, value)
validation = validation_for(record.country)
return unless validation
valid = validation.call(value)
record.errors.add(attribute, :invalid_national_id, country: record.country) unless valid
end
private
def validation_for(country)
self.class.country_specific_validations[country]
end
end

View file

@ -0,0 +1,21 @@
class Contact::Ident::RegNoValidator < ActiveModel::EachValidator
def self.country_specific_formats
{
Country.new('EE') => /\A[0-9]{8}\z/,
}
end
def validate_each(record, attribute, value)
format = format_for(record.country)
return unless format
record.errors.add(attribute, :invalid_reg_no, country: record.country) unless value =~ format
end
private
def format_for(country)
self.class.country_specific_formats[country]
end
end

View file

@ -19,7 +19,7 @@
.col-md-3 .col-md-3
.form-group .form-group
= label_tag t(:ident_type) = label_tag t(:ident_type)
= select_tag '[q][ident_type_eq]', options_for_select(Contact::IDENT_TYPES, params[:q][:ident_type_eq]), { include_blank: true, placeholder: t(:choose), class: 'form-control selectize' } = select_tag '[q][ident_type_eq]', options_for_select(ident_types, params[:q][:ident_type_eq]), { include_blank: true, placeholder: t(:choose), class: 'form-control selectize' }
.row .row
.col-md-3 .col-md-3
.form-group .form-group
@ -75,7 +75,7 @@
.row .row
.col-md-12 .col-md-12
.table-responsive .table-responsive
%table.table.table-hover.table-bordered.table-condensed %table.table.table-hover.table-bordered.table-condensed.contacts
%thead %thead
%tr %tr
%th{class: 'col-xs-2'} %th{class: 'col-xs-2'}

View file

@ -21,7 +21,7 @@
.col-md-3 .col-md-3
.form-group .form-group
= label_tag t(:ident_type) = label_tag t(:ident_type)
= select_tag '[q][ident_type_eq]', options_for_select(Contact::IDENT_TYPES, params[:q][:ident_type_eq]), { include_blank: true, placeholder: t(:choose), class: 'form-control selectize' } = select_tag '[q][ident_type_eq]', options_for_select(ident_types, params[:q][:ident_type_eq]), { include_blank: true, placeholder: t(:choose), class: 'form-control selectize' }
.row .row
.col-md-3 .col-md-3
.form-group .form-group
@ -85,7 +85,7 @@
.row .row
.col-md-12 .col-md-12
.table-responsive .table-responsive
%table.table.table-hover.table-bordered.table-condensed %table.table.table-hover.table-bordered.table-condensed.contacts
%thead %thead
%tr %tr
%th{class: 'col-xs-2'} %th{class: 'col-xs-2'}

View file

@ -1,20 +0,0 @@
# Log all active model user errors
# rubocop: disable Lint/AssignmentInCondition
# rubocop: disable Style/SignalException
module ActiveModel
class Errors
def add(attribute, message = :invalid, options = {})
message = normalize_message(attribute, message, options)
if exception = options[:strict]
exception = ActiveModel::StrictValidationFailed if exception == true
raise exception, full_message(attribute, message)
end
# CUSTOM logging
Rails.logger.info "USER MSG: ACTIVEMODEL: #{@base.try(:class)} [#{attribute}] #{message}" if message.present?
# END of CUSTOM logging
self[attribute] << message
end
end
end

View file

@ -1,31 +0,0 @@
# Log all flash messages
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/LineLength
module ActionDispatch
class Flash
# rubocop: disable Metrics/PerceivedComplexity
# rubocop: disable Style/MultilineOperationIndentation
def call(env)
@app.call(env)
ensure
session = Request::Session.find(env) || {}
flash_hash = env[KEY]
if flash_hash && (flash_hash.present? || session.key?('flash'))
session["flash"] = flash_hash.to_session_value
# EIS custom logging
Rails.logger.info "USER MSG: FLASH: #{session['flash']['flashes'].inspect}" if session['flash']
# END OF EIS custom logging
env[KEY] = flash_hash.dup
end
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
session.key?('flash') && session['flash'].nil?
session.delete('flash')
end
end
end
end

View file

@ -9,5 +9,20 @@ en:
models: models:
contact: contact:
attributes: attributes:
code:
blank: "Required parameter missing - code"
too_long_contact_code: "Contact code is too long, max 100 characters"
name:
blank: "Required parameter missing - name"
phone:
blank: "Required parameter missing - phone"
invalid: "Phone nr is invalid"
email:
blank: "Required parameter missing - email"
invalid: "Email is invalid"
domains:
exist: 'Object association prohibits operation'
statuses:
not_uniq: 'not uniq'
country_code: country_code:
invalid: Country code is not valid, should be in ISO_3166-1 alpha 2 format invalid: Country code is not valid, should be in ISO_3166-1 alpha 2 format

View file

@ -34,33 +34,6 @@ en:
activerecord: activerecord:
errors: errors:
models: models:
contact:
attributes:
code:
blank: "Required parameter missing - code"
too_long_contact_code: "Contact code is too long, max 100 characters"
name:
blank: "Required parameter missing - name"
phone:
blank: "Required parameter missing - phone"
invalid: "Phone nr is invalid"
email:
blank: "Required parameter missing - email"
invalid: "Email is invalid"
ident:
blank: "Required parameter missing - ident"
invalid_EE_identity_format: "Ident not in valid Estonian identity format."
invalid_EE_identity_format_update: "Ident not in valid Estonian identity format. Please create new contact"
invalid_birthday_format: "Ident not in valid birthady format, should be YYYY-MM-DD"
invalid_country_code: "Ident country code is not valid, should be in ISO_3166-1 alpha 2 format"
ident_type:
ident_type_invalid: 'Ident type is invalid'
epp_ident_type_invalid: 'Object status prohibits operation: ident_type of contact %{code} is invalid'
domains:
exist: 'Object association prohibits operation'
statuses:
not_uniq: 'not uniq'
epp_domain: &epp_domain_ar_attributes epp_domain: &epp_domain_ar_attributes
attributes: attributes:
base: base:
@ -262,6 +235,8 @@ en:
unimplemented_command: 'Unimplemented command' unimplemented_command: 'Unimplemented command'
domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar' domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar'
required_ident_attribute_missing: "Required ident attribute missing: %{key}" required_ident_attribute_missing: "Required ident attribute missing: %{key}"
invalid_iso31661_alpha2: does not conform to ISO 3166-1 alpha-2 standard
invalid_iso8601_date: has invalid date format YYYY-MM-DD (ISO 8601)
code: 'Code' code: 'Code'
action: 'Action' action: 'Action'
@ -487,7 +462,6 @@ en:
crt_revoked: 'CRT (revoked)' crt_revoked: 'CRT (revoked)'
contact_org_error: 'Parameter value policy error. Org must be blank' contact_org_error: 'Parameter value policy error. Org must be blank'
contact_fax_error: 'Parameter value policy error. Fax must be blank' contact_fax_error: 'Parameter value policy error. Fax must be blank'
ident_update_error: 'Parameter value policy error. Update of ident data not allowed [ident]'
invoices: 'Invoices' invoices: 'Invoices'
no_such_user: 'No such user' no_such_user: 'No such user'
phone_no: 'Phone number' phone_no: 'Phone number'

View file

@ -3,3 +3,11 @@ en:
contacts: contacts:
completed: Command completed successfully completed: Command completed successfully
completed_without_address: Command completed successfully; Postal address data discarded completed_without_address: Command completed successfully; Postal address data discarded
errors:
valid_ident: >-
Ident update is not allowed.
Consider creating new contact object
ident_update: >-
Only ident type and country can be updated in case of invalid ident.
Please create new contact object to update ident code

11
config/locales/idents.yml Normal file
View file

@ -0,0 +1,11 @@
en:
activemodel:
errors:
models:
contact/ident:
attributes:
base:
mismatch: Ident type "%{type}" is invalid for %{country}
code:
invalid_national_id: does not conform to national identification number format of %{country}
invalid_reg_no: does not conform to registration number format of %{country}

View file

@ -36,7 +36,7 @@ More info: https://en.wikipedia.org/wiki/Latin_script_in_Unicode
<contact:email> 1 E-mail <contact:email> 1 E-mail
<extension> 1 <extension> 1
<eis:extdata> 1 Attribute: xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd" <eis:extdata> 1 Attribute: xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd"
<eis:ident> 1 Contact identificator <eis:ident> 1 Identifier
Attribute: "type" Attribute: "type"
"org" # Business registry code "org" # Business registry code
"priv" # National identification number "priv" # National identification number
@ -73,7 +73,7 @@ More info: https://en.wikipedia.org/wiki/Latin_script_in_Unicode
<contact:pw> 1 Contact password. Attribute: roid="String" <contact:pw> 1 Contact password. Attribute: roid="String"
<extension> 0-1 <extension> 0-1
<eis:extdata> 0-1 Attribute: xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd" <eis:extdata> 0-1 Attribute: xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd"
<eis:ident> 0-1 Contact identificator <eis:ident> 0-1 Identifier
Attribute: "type" Attribute: "type"
"org" # Business registry code "org" # Business registry code
"priv" # National identification number "priv" # National identification number

View file

@ -1031,25 +1031,11 @@
<polygon fill="#0374bc" stroke="#0374bc" points="2896.54,-311.549 2886.9,-306.334 2891.56,-311.185 2886.57,-310.822 2886.57,-310.822 2886.57,-310.822 2891.56,-311.185 2886.24,-315.31 2896.54,-311.549 2896.54,-311.549"/> <polygon fill="#0374bc" stroke="#0374bc" points="2896.54,-311.549 2886.9,-306.334 2891.56,-311.185 2886.57,-310.822 2886.57,-310.822 2886.57,-310.822 2891.56,-311.185 2886.24,-315.31 2896.54,-311.549 2896.54,-311.549"/>
</g> </g>
<!-- Registrar&#45;&gt;Contact --> <!-- Registrar&#45;&gt;Contact -->
<g id="edge68" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#08d288" d="M1289.24,-522.039C1610.25,-522.115 3337.74,-519.996 3565.99,-466 3599.47,-458.082 3606.71,-451.045 3635.99,-433 3658.1,-419.38 3659.77,-410.424 3681.99,-397 3698.68,-386.919 3711.41,-395.371 3721.99,-379 3772.14,-301.446 3754.66,-355.133 3112.99,-325 3037.3,-321.445 2949.51,-315.411 2896.66,-311.57"/>
<ellipse fill="none" stroke="#08d288" cx="1285.04" cy="-522.038" rx="4" ry="4"/>
<polygon fill="#08d288" stroke="#08d288" points="2896.54,-311.561 2886.9,-306.344 2891.56,-311.197 2886.57,-310.832 2886.57,-310.832 2886.57,-310.832 2891.56,-311.197 2886.24,-315.32 2896.54,-311.561 2896.54,-311.561"/>
<text text-anchor="middle" x="3718.49" y="-411.3" font-family="Times,serif" font-size="14.00">priv_contacts</text>
</g>
<!-- Registrar&#45;&gt;Contact -->
<g id="edge105" class="edge"><title>Registrar&#45;&gt;Contact</title> <g id="edge105" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#983d43" d="M1289.27,-522.186C1639.37,-523.21 3666.41,-525.142 3754.99,-433 3768.3,-419.16 3746.5,-345.108 3743.99,-343 3717.15,-320.429 3148.03,-326.627 3112.99,-325 3037.29,-321.484 2949.51,-315.438 2896.66,-311.584"/> <path fill="none" stroke="#983d43" d="M1289.27,-522.186C1639.37,-523.21 3666.41,-525.142 3754.99,-433 3768.3,-419.16 3746.5,-345.108 3743.99,-343 3717.15,-320.429 3148.03,-326.627 3112.99,-325 3037.29,-321.484 2949.51,-315.438 2896.66,-311.584"/>
<ellipse fill="none" stroke="#983d43" cx="1285.07" cy="-522.173" rx="4" ry="4"/> <ellipse fill="none" stroke="#983d43" cx="1285.07" cy="-522.173" rx="4" ry="4"/>
<polygon fill="#983d43" stroke="#983d43" points="2896.54,-311.576 2886.9,-306.356 2891.56,-311.21 2886.57,-310.844 2886.57,-310.844 2886.57,-310.844 2891.56,-311.21 2886.24,-315.332 2896.54,-311.576 2896.54,-311.576"/> <polygon fill="#983d43" stroke="#983d43" points="2896.54,-311.576 2886.9,-306.356 2891.56,-311.21 2886.57,-310.844 2886.57,-310.844 2886.57,-310.844 2891.56,-311.21 2886.24,-315.332 2896.54,-311.576 2896.54,-311.576"/>
</g> </g>
<!-- Registrar&#45;&gt;Contact -->
<g id="edge112" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#aa3e5f" d="M1289.02,-521.999C1518.6,-521.522 2478.68,-514.494 3260.99,-433 3260.99,-433 3649.99,-379 3649.99,-379 3710.03,-310.049 3826.1,-359.903 3112.99,-325 3037.3,-321.295 2949.51,-315.304 2896.66,-311.513"/>
<ellipse fill="none" stroke="#aa3e5f" cx="1284.84" cy="-522.006" rx="4" ry="4"/>
<polygon fill="#aa3e5f" stroke="#aa3e5f" points="2896.55,-311.504 2886.89,-306.297 2891.56,-311.145 2886.57,-310.785 2886.57,-310.785 2886.57,-310.785 2891.56,-311.145 2886.25,-315.273 2896.55,-311.504 2896.55,-311.504"/>
<text text-anchor="middle" x="3555.49" y="-411.3" font-family="Times,serif" font-size="14.00">priv_contacts</text>
</g>
<!-- Registrar&#45;&gt;Domain --> <!-- Registrar&#45;&gt;Domain -->
<g id="edge60" class="edge"><title>Registrar&#45;&gt;Domain</title> <g id="edge60" class="edge"><title>Registrar&#45;&gt;Domain</title>
<path fill="none" stroke="#a7f439" d="M1285.22,-514.145C1410.96,-494.146 1752.36,-441.557 1878.45,-422.782"/> <path fill="none" stroke="#a7f439" d="M1285.22,-514.145C1410.96,-494.146 1752.36,-441.557 1878.45,-422.782"/>

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Before After
Before After

View file

@ -1643,25 +1643,11 @@
<polygon fill="#257f99" stroke="#257f99" points="3166.6,-1436.81 3164.86,-1425.98 3163.85,-1432.63 3161.1,-1428.45 3161.1,-1428.45 3161.1,-1428.45 3163.85,-1432.63 3157.34,-1430.93 3166.6,-1436.81 3166.6,-1436.81"/> <polygon fill="#257f99" stroke="#257f99" points="3166.6,-1436.81 3164.86,-1425.98 3163.85,-1432.63 3161.1,-1428.45 3161.1,-1428.45 3161.1,-1428.45 3163.85,-1432.63 3157.34,-1430.93 3166.6,-1436.81 3166.6,-1436.81"/>
</g> </g>
<!-- Registrar&#45;&gt;Contact --> <!-- Registrar&#45;&gt;Contact -->
<g id="edge68" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#625a51" d="M1737.79,-2486.74C2105.37,-2477.42 3671.93,-2427.41 3783,-2250 3886.64,-2084.47 3711.19,-1968.76 3829,-1813 3841.99,-1795.82 3861.92,-1812.84 3874,-1795 3894.81,-1764.27 3899.79,-1654.68 3874,-1628 3833.13,-1585.71 3397.45,-1636.61 3345,-1610 3270.62,-1572.26 3210.61,-1502.97 3166.63,-1436.95"/>
<ellipse fill="none" stroke="#625a51" cx="1733.59" cy="-2486.84" rx="4" ry="4"/>
<polygon fill="#625a51" stroke="#625a51" points="3166.57,-1436.86 3164.84,-1426.03 3163.82,-1432.68 3161.08,-1428.5 3161.08,-1428.5 3161.08,-1428.5 3163.82,-1432.68 3157.32,-1430.98 3166.57,-1436.86 3166.57,-1436.86"/>
<text text-anchor="middle" x="3865.5" y="-2027.8" font-family="Times,serif" font-size="14.00">priv_contacts</text>
</g>
<!-- Registrar&#45;&gt;Contact -->
<g id="edge105" class="edge"><title>Registrar&#45;&gt;Contact</title> <g id="edge105" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#84dd57" d="M1737.74,-2486.28C2114.24,-2474.64 3753.89,-2414.71 3902,-2250 3948.21,-2198.61 3944.87,-1676.88 3896,-1628 3852.69,-1584.68 3399.64,-1637.68 3345,-1610 3270.59,-1572.31 3210.58,-1503.03 3166.61,-1437"/> <path fill="none" stroke="#84dd57" d="M1737.74,-2486.28C2114.24,-2474.64 3753.89,-2414.71 3902,-2250 3948.21,-2198.61 3944.87,-1676.88 3896,-1628 3852.69,-1584.68 3399.64,-1637.68 3345,-1610 3270.59,-1572.31 3210.58,-1503.03 3166.61,-1437"/>
<ellipse fill="none" stroke="#84dd57" cx="1733.61" cy="-2486.41" rx="4" ry="4"/> <ellipse fill="none" stroke="#84dd57" cx="1733.61" cy="-2486.41" rx="4" ry="4"/>
<polygon fill="#84dd57" stroke="#84dd57" points="3166.55,-1436.91 3164.81,-1426.08 3163.8,-1432.73 3161.05,-1428.55 3161.05,-1428.55 3161.05,-1428.55 3163.8,-1432.73 3157.29,-1431.02 3166.55,-1436.91 3166.55,-1436.91"/> <polygon fill="#84dd57" stroke="#84dd57" points="3166.55,-1436.91 3164.81,-1426.08 3163.8,-1432.73 3161.05,-1428.55 3161.05,-1428.55 3161.05,-1428.55 3163.8,-1432.73 3157.29,-1431.02 3166.55,-1436.91 3166.55,-1436.91"/>
</g> </g>
<!-- Registrar&#45;&gt;Contact -->
<g id="edge112" class="edge"><title>Registrar&#45;&gt;Contact</title>
<path fill="none" stroke="#eb888a" d="M1738.05,-2484.46C2024.22,-2468.39 3023.5,-2402.27 3298,-2250 3520.54,-2126.55 3443.92,-1933.63 3668,-1813 3697.56,-1797.09 3795.72,-1820.11 3818,-1795 3842.63,-1767.24 3843.74,-1654.74 3818,-1628 3781.52,-1590.11 3391.85,-1633.89 3345,-1610 3270.7,-1572.11 3210.7,-1502.8 3166.7,-1436.81"/>
<ellipse fill="none" stroke="#eb888a" cx="1733.79" cy="-2484.7" rx="4" ry="4"/>
<polygon fill="#eb888a" stroke="#eb888a" points="3166.64,-1436.72 3164.91,-1425.89 3163.9,-1432.54 3161.15,-1428.37 3161.15,-1428.37 3161.15,-1428.37 3163.9,-1432.54 3157.39,-1430.84 3166.64,-1436.72 3166.64,-1436.72"/>
<text text-anchor="middle" x="3704.5" y="-2027.8" font-family="Times,serif" font-size="14.00">priv_contacts</text>
</g>
<!-- Registrar&#45;&gt;Domain --> <!-- Registrar&#45;&gt;Domain -->
<g id="edge60" class="edge"><title>Registrar&#45;&gt;Domain</title> <g id="edge60" class="edge"><title>Registrar&#45;&gt;Domain</title>
<path fill="none" stroke="#6a2f42" d="M1734.92,-2400.64C1792.79,-2335.05 1874.64,-2244.16 1943.33,-2169.11"/> <path fill="none" stroke="#6a2f42" d="M1734.92,-2400.64C1792.79,-2335.05 1874.64,-2244.16 1943.33,-2169.11"/>

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Before After
Before After

View file

@ -174,6 +174,8 @@ namespace :dev do
end end
end end
Setting.api_ip_whitelist_enabled = false
Setting.address_processing = false
Setting.registrar_ip_whitelist_enabled = false Setting.registrar_ip_whitelist_enabled = false
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do

View file

@ -0,0 +1,12 @@
class E164Validator < ActiveModel::EachValidator
def validate_each(record, attribute, _value)
length_validator = ActiveModel::Validations::
LengthValidator.new(maximum: 17, attributes: attribute)
length_validator.validate(record)
format_validator = ActiveModel::Validations::
FormatValidator.new(with: /\+[0-9]{1,3}\.[0-9]{1,14}?/,
attributes: attribute)
format_validator.validate(record)
end
end

View file

@ -0,0 +1,11 @@
class Iso31661Alpha2Validator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, :invalid_iso31661_alpha2) unless valid_country_code?(value)
end
private
def valid_country_code?(country_code)
Country.new(country_code)
end
end

View file

@ -0,0 +1,13 @@
class Iso8601Validator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if options[:date_only]
record.errors.add(attribute, :invalid_iso8601_date) unless value =~ date_format
end
end
private
def date_format
/\d{4}-\d{2}-\d{2}/
end
end

View file

@ -2,11 +2,11 @@ class PhoneValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
return if record.errors[:phone].any? return if record.errors[:phone].any?
splitted_phone = value.split('.') phone_parts = value.split('.')
country_code = splitted_phone.first country_code = phone_parts.first
phone_number = splitted_phone.second subscriber_no = phone_parts.second
if zeros_only?(country_code) || zeros_only?(phone_number) if zeros_only?(country_code) || zeros_only?(subscriber_no)
record.errors.add(attribute, :invalid) record.errors.add(attribute, :invalid)
end end
end end

View file

@ -0,0 +1,12 @@
require 'rails_helper'
RSpec.feature 'Contact list', settings: false do
background do
sign_in_to_admin_area
end
it 'is visible' do
visit admin_contacts_path
expect(page).to have_css('.contacts')
end
end

View file

@ -0,0 +1,15 @@
require 'rails_helper'
RSpec.feature 'Contact list', settings: false do
given!(:registrar) { create(:registrar) }
given!(:contact) { create(:contact, registrar: registrar) }
background do
sign_in_to_registrar_area(user: create(:api_user_with_unlimited_balance, registrar: registrar))
end
it 'is visible' do
visit registrar_contacts_path
expect(page).to have_css('.contacts')
end
end

View file

@ -0,0 +1,29 @@
# https://en.wikipedia.org/wiki/E.164
RSpec.shared_examples 'e164' do
describe 'validation' do
it 'rejects invalid format' do
model.send("#{attribute}=", '+.1')
model.validate
expect(model.errors).to be_added(attribute, :invalid)
end
it 'rejects longer than max length' do
model.send("#{attribute}=", '1' * 18)
model.validate
expect(model.errors).to be_added(attribute, :too_long, count: 17)
end
it 'accepts valid format' do
model.send("#{attribute}=", '+123.4')
model.validate
expect(model.errors).to_not be_added(attribute, :invalid)
end
it 'accepts max length' do
model.send("#{attribute}=", '1' * 17)
model.validate
expect(model.errors).to_not be_added(attribute, :too_long, count: 17)
end
end
end

View file

@ -0,0 +1,17 @@
# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
RSpec.shared_examples 'iso31661_alpha2' do
describe 'validation' do
it 'rejects invalid' do
model.send("#{attribute}=", 'invalid')
model.validate
expect(model.errors).to be_added(attribute, :invalid_iso31661_alpha2)
end
it 'accepts valid' do
model.send("#{attribute}=", 'US')
model.validate
expect(model.errors).to_not be_added(attribute, :invalid_iso31661_alpha2)
end
end
end

View file

@ -0,0 +1,17 @@
# https://en.wikipedia.org/wiki/ISO_8601
RSpec.shared_examples 'iso8601' do
describe 'validation' do
it 'rejects invalid' do
model.send("#{attribute}=", '2010-07-0')
model.validate
expect(model.errors).to be_added(attribute, :invalid_iso8601_date)
end
it 'accepts valid' do
model.send("#{attribute}=", '2010-07-05')
model.validate
expect(model.errors).to_not be_added(attribute, :invalid_iso8601_date)
end
end
end

View file

@ -0,0 +1,33 @@
require 'rails_helper'
require 'lib/validators/e164'
RSpec.describe Contact do
let(:contact) { described_class.new }
describe 'phone', db: false do
it_behaves_like 'e164' do
let(:model) { contact }
let(:attribute) { :phone }
end
end
describe 'phone validation', db: false do
it 'rejects absent' do
contact.phone = nil
contact.validate
expect(contact.errors).to be_added(:phone, :blank)
end
it 'rejects all zeros in country code' do
contact.phone = '+000.1'
contact.validate
expect(contact.errors).to be_added(:phone, :invalid)
end
it 'rejects all zeros in subscriber number' do
contact.phone = '+123.0'
contact.validate
expect(contact.errors).to be_added(:phone, :invalid)
end
end
end

View file

@ -0,0 +1,227 @@
require 'active_model'
require 'lib/validators/iso31661_alpha2'
require 'lib/validators/iso8601'
RSpec.describe Contact::Ident, db: false do
let(:ident) { described_class.new }
describe 'country code' do
it_behaves_like 'iso31661_alpha2' do
let(:model) { ident }
let(:attribute) { :country_code }
end
end
describe 'code validation' do
it 'rejects absent' do
ident.code = nil
ident.validate
expect(ident.errors).to be_added(:code, :blank)
end
context 'when type is :birthday' do
let(:ident) { described_class.new(type: 'birthday') }
it_behaves_like 'iso8601' do
let(:model) { ident }
let(:attribute) { :code }
end
end
context 'when type is not :birthday' do
let(:ident) { described_class.new(type: 'priv') }
it 'accepts any' do
ident.code = '%123456789%'
ident.validate
expect(ident.errors).to_not include(:code)
end
end
context 'when country code is EE' do
context 'when type is :priv' do
let(:ident) { described_class.new(country_code: 'EE', type: 'priv') }
it 'rejects invalid' do
ident.code = 'invalid'
ident.validate
expect(ident.errors).to be_added(:code, :invalid_national_id, country: 'Estonia')
end
it 'accepts valid' do
ident.code = '47101010033'
ident.validate
expect(ident.errors).to_not be_added(:code, :invalid_national_id, country: 'Estonia')
end
end
context 'when ident type is :org' do
let(:ident) { described_class.new(country_code: 'EE', type: 'org') }
it 'rejects invalid' do
ident.code = '1' * 7
ident.validate
expect(ident.errors).to be_added(:code, :invalid_reg_no, country: 'Estonia')
end
it 'accepts valid length' do
ident.code = '1' * 8
ident.validate
expect(ident.errors).to_not be_added(:code, :invalid_reg_no, country: 'Estonia')
end
end
end
context 'when ident country code is not EE' do
let(:ident) { described_class.new(country_code: 'US') }
it 'accepts any' do
ident.code = 'test-123456789'
ident.validate
expect(ident.errors).to_not include(:code)
end
end
it 'translates :invalid_national_id error message' do
expect(ident.errors.generate_message(:code, :invalid_national_id, country: 'Germany'))
.to eq('does not conform to national identification number format of Germany')
end
it 'translates :invalid_reg_no error message' do
expect(ident.errors.generate_message(:code, :invalid_reg_no, country: 'Germany'))
.to eq('does not conform to registration number format of Germany')
end
end
describe 'type validation' do
before do
allow(described_class).to receive(:types).and_return(%w(valid))
end
it 'rejects absent' do
ident.type = nil
ident.validate
expect(ident.errors).to be_added(:type, :blank)
end
it 'rejects invalid' do
ident.type = 'invalid'
ident.validate
expect(ident.errors).to be_added(:type, :inclusion)
end
it 'accepts valid' do
ident.type = 'valid'
ident.validate
expect(ident.errors).to_not be_added(:type, :inclusion)
end
end
describe 'country code validation' do
it 'rejects absent' do
ident.country_code = nil
ident.validate
expect(ident.errors).to be_added(:country_code, :blank)
end
end
describe 'mismatch validation' do
let(:ident) { described_class.new(type: 'test', country_code: 'DE') }
before do
mismatches = [Contact::Ident::MismatchValidator::Mismatch.new('test', Country.new('DE'))]
allow(Contact::Ident::MismatchValidator).to receive(:mismatches).and_return(mismatches)
end
it 'rejects mismatched' do
ident.validate
expect(ident.errors).to be_added(:base, :mismatch, type: 'test', country: 'Germany')
end
it 'accepts matched' do
ident.validate
expect(ident.errors).to_not be_added(:base, :mismatch, type: 'another-test', country: 'Germany')
end
it 'translates :mismatch error message' do
expect(ident.errors.generate_message(:base, :mismatch, type: 'test', country: 'Germany'))
.to eq('Ident type "test" is invalid for Germany')
end
end
describe '::types' do
it 'returns types' do
types = %w[
org
priv
birthday
]
expect(described_class.types).to eq(types)
end
end
describe '#birthday?' do
context 'when type is birthday' do
subject(:ident) { described_class.new(type: 'birthday') }
it { is_expected.to be_birthday }
end
context 'when type is not birthday' do
subject(:ident) { described_class.new(type: 'priv') }
it { is_expected.to_not be_birthday }
end
end
describe '#national_id?' do
context 'when type is priv' do
subject(:ident) { described_class.new(type: 'priv') }
it { is_expected.to be_national_id }
end
context 'when type is not' do
subject(:ident) { described_class.new(type: 'org') }
it { is_expected.to_not be_national_id }
end
end
describe '#reg_no?' do
context 'when type is birthday' do
subject(:ident) { described_class.new(type: 'org') }
it { is_expected.to be_reg_no }
end
context 'when type is not birthday' do
subject(:ident) { described_class.new(type: 'priv') }
it { is_expected.to_not be_reg_no }
end
end
describe '#country' do
let(:ident) { described_class.new(country_code: 'US') }
it 'returns country' do
expect(ident.country).to eq(Country.new('US'))
end
end
describe '#==' do
let(:ident) { described_class.new(code: 'test', type: 'test', country_code: 'US') }
context 'when code, type and country code are the same' do
let(:another_ident) { described_class.new(code: 'test', type: 'test', country_code: 'US') }
it 'returns true' do
expect(ident).to eq(another_ident)
end
end
context 'when code, type and country code are not the same' do
let(:another_ident) { described_class.new(code: 'another-test', type: 'test', country_code: 'US') }
it 'returns false' do
expect(ident).to_not eq(another_ident)
end
end
end
end

View file

@ -28,45 +28,6 @@ RSpec.describe Contact do
@contact.updator.should == nil @contact.updator.should == nil
end end
it 'should require country code when org' do
@contact.ident_type = 'org'
@contact.valid?
@contact.errors[:ident_country_code].should == ['is missing']
end
it 'should require country code when priv' do
@contact.ident_type = 'priv'
@contact.valid?
@contact.errors[:ident_country_code].should == ['is missing']
end
it 'should validate correct country code' do
@contact.ident = 1
@contact.ident_type = 'org'
@contact.ident_country_code = 'EE'
@contact.valid?
@contact.errors[:ident_country_code].should == []
end
it 'should require valid country code' do
@contact.ident = '123'
@contact.ident_type = 'org'
@contact.ident_country_code = 'INVALID'
@contact.valid?
expect(@contact.errors).to have_key(:ident)
end
it 'should convert to alpha2 country code' do
@contact.ident = 1
@contact.ident_type = 'org'
@contact.ident_country_code = 'ee'
@contact.validate
@contact.ident_country_code.should == 'EE'
end
it 'should not have any versions' do it 'should not have any versions' do
@contact.versions.should == [] @contact.versions.should == []
end end
@ -119,14 +80,6 @@ RSpec.describe Contact do
@contact.domains_present?.should == false @contact.domains_present?.should == false
end end
it 'org should be valid' do
contact = Fabricate.build(:contact, ident_type: 'org', ident: '1' * 8)
contact.validate
contact.errors.full_messages.should match_array([])
end
it 'should not overwrite code' do it 'should not overwrite code' do
old_code = @contact.code old_code = @contact.code
@contact.code = 'CID:REG1:should-not-overwrite-old-code-12345' @contact.code = 'CID:REG1:should-not-overwrite-old-code-12345'
@ -217,31 +170,6 @@ RSpec.describe Contact do
end end
end end
context 'as birthday' do
before :example do
@contact.ident_type = 'birthday'
end
it 'birthday should be valid' do
valid = ['2012-12-11', '1990-02-16']
valid.each do |date|
@contact.ident = date
@contact.valid?
@contact.errors.full_messages.should match_array([])
end
end
it 'birthday should be invalid' do
invalid = ['123' '12/12/2012', 'aaaa', '12/12/12', '02-11-1999']
invalid.each do |date|
@contact.ident = date
@contact.valid?
@contact.errors.full_messages.should ==
["Ident Ident not in valid birthady format, should be YYYY-MM-DD"]
end
end
end
context 'with callbacks' do context 'with callbacks' do
before :example do before :example do
# Ensure callbacks are not taken out from other specs # Ensure callbacks are not taken out from other specs
@ -445,7 +373,7 @@ RSpec.describe Contact do
end end
end end
describe 'country code validation' do describe 'country code validation', db: false do
let(:contact) { described_class.new(country_code: 'test') } let(:contact) { described_class.new(country_code: 'test') }
it 'rejects invalid' do it 'rejects invalid' do
@ -455,37 +383,25 @@ RSpec.describe Contact do
end end
end end
describe 'phone validation', db: false do describe 'identifier validation', db: false do
let(:contact) { described_class.new } let(:contact) { described_class.new }
it 'rejects absent' do it 'rejects invalid' do
contact.phone = nil ident = Contact::Ident.new
ident.validate
contact.identifier = ident
contact.validate contact.validate
expect(contact.errors).to have_key(:phone)
end
it 'rejects invalid format' do expect(contact.errors).to be_added(:identifier, :invalid)
contact.phone = '123'
contact.validate
expect(contact.errors).to have_key(:phone)
end
it 'rejects all zeros in country code' do
contact.phone = '+000.1'
contact.validate
expect(contact.errors).to have_key(:phone)
end
it 'rejects all zeros in phone number' do
contact.phone = '+123.0'
contact.validate
expect(contact.errors).to have_key(:phone)
end end
it 'accepts valid' do it 'accepts valid' do
contact.phone = '+123.4' ident = Contact::Ident.new(code: 'test', type: 'priv', country_code: 'US')
ident.validate
contact.identifier = ident
contact.validate contact.validate
expect(contact.errors).to_not have_key(:phone)
expect(contact.errors).to_not be_added(:identifier, :invalid)
end end
end end
@ -595,4 +511,13 @@ RSpec.describe Contact do
expect(domain_names).to eq({ 'test.com' => %i[admin_domain_contact].to_set }) expect(domain_names).to eq({ 'test.com' => %i[admin_domain_contact].to_set })
end end
end end
it 'normalizes ident country code', db: false do
contact = described_class.new
contact.ident_country_code = 'ee'
contact.validate
expect(contact.ident_country_code).to eq('EE')
end
end end

View file

@ -0,0 +1,21 @@
require 'rails_helper'
RSpec.describe EPP::Response::Result, db: false do
# https://tools.ietf.org/html/rfc5730#section-3
describe '::codes' do
it 'returns codes' do
codes = {
'1000' => :success,
'1001' => :success_pending,
'1300' => :success_empty_queue,
'1301' => :success_dequeue,
'2001' => :syntax_error,
'2003' => :required_param_missing,
'2005' => :param_syntax_error,
'2308' => :data_management_policy_violation
}
expect(described_class.codes).to eq(codes)
end
end
end

View file

@ -35,10 +35,6 @@ describe Registrar do
@registrar.reference_no.should_not be_blank @registrar.reference_no.should_not be_blank
@registrar.reference_no.last(10).to_i.should_not == 0 @registrar.reference_no.last(10).to_i.should_not == 0
end end
it 'should not have priv contacts' do
@registrar.priv_contacts.size.should == 0
end
end end
context 'with valid attributes' do context 'with valid attributes' do
@ -120,9 +116,5 @@ describe Registrar do
registrar.valid? registrar.valid?
registrar.errors.full_messages.should == ['Code is forbidden to use'] registrar.errors.full_messages.should == ['Code is forbidden to use']
end end
it 'should not have priv contacts' do
@registrar.priv_contacts.size.should == 0
end
end end
end end

View file

@ -10,6 +10,7 @@ require 'support/requests/epp_helpers'
require 'support/features/session_helpers' require 'support/features/session_helpers'
require 'support/matchers/alias_attribute' require 'support/matchers/alias_attribute'
require 'support/matchers/epp/code' require 'support/matchers/epp/code'
require 'support/matchers/epp/have_result'
require 'support/capybara' require 'support/capybara'
require 'support/devise' require 'support/devise'
@ -28,7 +29,8 @@ RSpec.configure do |config|
config.include AbstractController::Translation, type: :request config.include AbstractController::Translation, type: :request
config.include AbstractController::Translation, type: :feature config.include AbstractController::Translation, type: :feature
config.include AbstractController::Translation, type: :mailer config.include AbstractController::Translation, type: :mailer
config.include Requests::EPPHelpers, type: :request config.include Requests::EPPHelpers, epp: true
config.include Matchers::EPP, epp: true
config.define_derived_metadata(file_path: %r[/spec/features/]) do |metadata| config.define_derived_metadata(file_path: %r[/spec/features/]) do |metadata|
metadata[:db] = true if metadata[:db].nil? metadata[:db] = true if metadata[:db].nil?

View file

@ -0,0 +1,289 @@
require 'rails_helper'
RSpec.describe 'EPP contact:create' do
let(:request) { post '/epp/command/create', frame: request_xml }
before do
Setting.address_processing = false
sign_in_to_epp_area
end
context 'when all ident params are valid' do
let(:ident) { Contact.first.identifier }
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv" cc="US">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'creates a contact' do
expect { request }.to change { Contact.count }.from(0).to(1)
end
it 'saves ident type' do
request
expect(ident.type).to eq('priv')
end
it 'saves ident country code' do
request
expect(ident.country_code).to eq('US')
end
specify do
request
expect(epp_response).to have_result(:success)
end
end
context 'when code is blank' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv" cc="US"></eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
expect(epp_response).to have_result(:required_param_missing,
'Required parameter missing: extension > extdata > ident [ident]')
end
end
context 'when code is not valid national id' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv" cc="DE">invalid</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
before do
country_specific_validations = {
Country.new('DE') => proc { false },
}
allow(Contact::Ident::NationalIdValidator).to receive(:country_specific_validations)
.and_return(country_specific_validations)
end
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
message = 'Ident code does not conform to national identification number format of Germany'
expect(epp_response).to have_result(:param_syntax_error, message)
end
end
context 'when code is not valid registration number' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="org" cc="DE">invalid</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
before do
country_specific_formats = {
Country.new('DE') => /\Avalid\z/,
}
allow(Contact::Ident::RegNoValidator).to receive(:country_specific_formats).and_return(country_specific_formats)
end
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
expect(epp_response).to have_result(:param_syntax_error,
'Ident code does not conform to registration number format of Germany')
end
end
context 'when country code is absent' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
expect(epp_response).to have_result(:required_param_missing,
'Required ident attribute missing: cc')
end
end
context 'when country code is blank' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv" cc="">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
expect(epp_response).to have_result(:syntax_error)
end
end
context 'when mismatches' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<create>
<contact:create xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
<contact:voice>+1.2</contact:voice>
<contact:email>test@test.com</contact:email>
</contact:create>
</create>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident type="priv" cc="DE">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
before do
mismatches = [
Contact::Ident::MismatchValidator::Mismatch.new('priv', Country.new('DE'))
]
allow(Contact::Ident::MismatchValidator).to receive(:mismatches).and_return(mismatches)
end
it 'does not create a contact' do
expect { request }.to_not change { Contact.count }
end
specify do
request
expect(epp_response).to have_result(:param_syntax_error,
'Ident type "priv" is invalid for Germany')
end
end
end

View file

@ -0,0 +1,196 @@
require 'rails_helper'
# https://github.com/internetee/registry/issues/576
RSpec.describe 'EPP contact:update' do
let(:ident) { contact.identifier }
let(:request) { post '/epp/command/update', frame: request_xml }
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<update>
<contact:update xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:id>TEST</contact:id>
<contact:chg>
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
</contact:chg>
</contact:update>
</update>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident cc="US" type="priv">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
before do
sign_in_to_epp_area
end
context 'when contact ident is valid' do
context 'when submitted ident matches current one' do
let!(:contact) { create(:contact, code: 'TEST',
ident: 'test',
ident_type: 'priv',
ident_country_code: 'US') }
specify do
request
expect(epp_response).to have_result(:success)
end
end
context 'when submitted ident does not match current one' do
let!(:contact) { create(:contact, code: 'TEST',
ident: 'another-test',
ident_type: 'priv',
ident_country_code: 'US') }
it 'does not update code' do
expect do
request
contact.reload
end.to_not change { ident.code }
end
it 'does not update type' do
expect do
request
contact.reload
end.to_not change { ident.type }
end
it 'does not update country code' do
expect do
request
contact.reload
end.to_not change { ident.country_code }
end
specify do
request
expect(epp_response).to have_result(:data_management_policy_violation,
t('epp.contacts.errors.valid_ident'))
end
end
end
context 'when contact ident is invalid' do
let(:contact) { build(:contact, code: 'TEST', ident: 'test', ident_type: nil, ident_country_code: nil) }
before do
contact.save(validate: false)
end
context 'when submitted ident is the same as current one' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<update>
<contact:update xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:id>TEST</contact:id>
<contact:chg>
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
</contact:chg>
</contact:update>
</update>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident cc="US" type="priv">test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'does not update code' do
expect do
request
contact.reload
end.to_not change { ident.code }
end
it 'updates type' do
request
contact.reload
expect(ident.type).to eq('priv')
end
it 'updates country code' do
request
contact.reload
expect(ident.country_code).to eq('US')
end
specify do
request
expect(epp_response).to have_result(:success)
end
end
context 'when submitted ident is different from current one' do
let(:request_xml) { <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="https://epp.tld.ee/schema/epp-ee-1.0.xsd">
<command>
<update>
<contact:update xmlns:contact="https://epp.tld.ee/schema/contact-ee-1.1.xsd">
<contact:id>TEST</contact:id>
<contact:chg>
<contact:postalInfo>
<contact:name>test</contact:name>
</contact:postalInfo>
</contact:chg>
</contact:update>
</update>
<extension>
<eis:extdata xmlns:eis="https://epp.tld.ee/schema/eis-1.0.xsd">
<eis:ident cc="US" type="priv">another-test</eis:ident>
</eis:extdata>
</extension>
</command>
</epp>
XML
}
it 'does not update code' do
expect do
request
contact.reload
end.to_not change { ident.code }
end
it 'does not update type' do
expect do
request
contact.reload
end.to_not change { ident.type }
end
it 'does not update country code' do
expect do
request
contact.reload
end.to_not change { ident.country_code }
end
specify do
request
expect(epp_response).to have_result(:data_management_policy_violation,
t('epp.contacts.errors.ident_update'))
end
end
end
end

View file

@ -0,0 +1,37 @@
module Matchers
module EPP
class HaveResultMatcher
def initialize(expected)
@expected = expected
end
def matches?(target)
@target = target
if @expected.message.present?
@target.results.any? { |result| result.code == @expected.code && result.message == @expected.message }
else
@target.results.any? { |result| result.code == @expected.code }
end
end
def failure_message
"expected #{@target.results} to have result #{@expected.inspect}"
end
def failure_message_when_negated
"expected #{@target.results} not to have result #{@expected.inspect}"
end
def description
"should have EPP code of #{@expected}"
end
end
def have_result(type, message = nil)
code = ::EPP::Response::Result.codes.key(type)
result = ::EPP::Response::Result.new(code, message)
HaveResultMatcher.new(result)
end
end
end

View file

@ -7,5 +7,9 @@ module Requests
def valid_legal_document def valid_legal_document
Base64.encode64('a' * 5000) Base64.encode64('a' * 5000)
end end
def epp_response
EPP::Response.from_xml(response.body)
end
end end
end end

View file

@ -0,0 +1,13 @@
require 'rails_helper'
RSpec.describe Contact::Ident::MismatchValidator do
describe '::mismatches' do
it 'returns mismatches' do
mismatches = [
Contact::Ident::MismatchValidator::Mismatch.new('birthday', Country.new('EE'))
]
expect(described_class.mismatches).to eq(mismatches)
end
end
end