diff --git a/.rubocop.yml b/.rubocop.yml index c58f51bdb..ed6970b34 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -77,3 +77,7 @@ Style/AsciiComments: # because NerdCommenter honors commented code intentions Style/CommentIndentation: Enabled: false + +# It did not alayws suggested good format +Style/AlignParameters: + Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ac4eee8..679be1f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -12.02.2015 +19.02.2015 Go to registry shared folder and setup CA directory tree: ``` diff --git a/Guardfile b/Guardfile index eab74ee21..e6d0a443a 100644 --- a/Guardfile +++ b/Guardfile @@ -3,11 +3,11 @@ group :red_green_refactor, halt_on_fail: true do # be sure you have apache2 configured to # accept EPP request on port 701, what proxy to 8989. # port and environment is just for correct notification, all is overwritten by CLI - guard :rails, port: 8989, environment: 'test' do - # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do - watch('Gemfile.lock') - watch(%r{^(config|lib)/.*}) - end + # guard :rails, port: 8989, environment: 'test' do + # # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do + # watch('Gemfile.lock') + # watch(%r{^(config|lib)/.*}) + # end guard :rspec, cmd: 'spring rspec', notification: false do watch(%r{^spec/.+_spec\.rb$}) @@ -18,6 +18,8 @@ group :red_green_refactor, halt_on_fail: true do watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^app/controllers/epp/(.+)_(controller)\.rb$}) { |m| ["spec/epp/#{m[1].sub(/s$/,'')}_spec.rb"] } + watch(%r{^app/models/epp/(.+)\.rb$}) { |m| "spec/epp/#{m[1]}_spec.rb" } watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch('config/routes.rb') { "spec/routing" } watch('app/controllers/application_controller.rb') { "spec/controllers" } diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 9810f7bdf..8bea17fdf 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,4 +1,3 @@ class AdminController < ApplicationController before_action :authenticate_user! - check_authorization end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 81acf1313..8840e4989 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,6 @@ class ApplicationController < ActionController::Base + check_authorization unless: :devise_controller? + # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception @@ -9,8 +11,14 @@ class ApplicationController < ActionController::Base params[resource] &&= send(method) if respond_to?(method, true) end + rescue_from CanCan::AccessDenied do |exception| + redirect_to admin_dashboard_path, alert: exception.message + end + def after_sign_in_path_for(_resource) - return session[:user_return_to].to_s if session[:user_return_to] && session[:user_return_to] != login_path + if session[:user_return_to] && session[:user_return_to] != login_path + return session[:user_return_to].to_s + end admin_dashboard_path end @@ -34,9 +42,3 @@ class ApplicationController < ActionController::Base end end end - -class ApplicationController < ActionController::Base - rescue_from CanCan::AccessDenied do |exception| - redirect_to admin_dashboard_path, alert: exception.message - end -end diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 6aa5efce3..5af05d9fd 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,188 +1,127 @@ class Epp::ContactsController < EppController - helper WhodunnitHelper ## Refactor this? - - def create - @contact = Contact.new(contact_and_address_attributes) - @contact.registrar = current_user.registrar - render_epp_response '/epp/contacts/create' and return if @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? && @contact.update_attributes(contact_and_address_attributes(:update)) - if owner? && @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 + before_action :find_contact, only: [:info, :update, :delete] + before_action :find_password, only: [:info, :update, :delete] 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 if @owner # for clarity, could just be true + authorize! :info, @contact, @password render_epp_response 'epp/contacts/info' end + def check + authorize! :check, Epp::Contact + + ids = params[:parsed_frame].css('id').map(&:text) + @results = Contact.check_availability(ids) + render_epp_response '/epp/contacts/check' + end + + def create + authorize! :create, Epp::Contact + + @contact = Epp::Contact.new(params[:parsed_frame]) + @contact.registrar = current_user.registrar + + if @contact.save + render_epp_response '/epp/contacts/create' + else + handle_errors(@contact) + end + end + + def update + authorize! :update, @contact, @password + + if @contact.update_attributes(params[:parsed_frame]) + render_epp_response 'epp/contacts/update' + else + handle_errors(@contact) + end + end + + def delete + authorize! :delete, @contact, @password + + if @contact.destroy_and_clean + render_epp_response '/epp/contacts/delete' + else + handle_errors(@contact) + end + end + def renew + authorize! :renew, Epp::Contact 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? + def find_password + @password = params[:parsed_frame].css('authInfo pw').text end - ## UPDATE - def validate_updatezz - @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 params[:parsed_frame].css('add').present? - return true if params[:parsed_frame].css('rem').present? - return true if params[: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_user.registrar == @contact.registrar || xml_attrs_present?(@ph, [%w(authInfo pw)]) - false - end - - ## SHARED - def find_contact - contact = Contact.find_by(code: @ph[:id]) - unless contact - epp_errors << { code: '2303', - msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: @ph[:id] } } + code = params[:parsed_frame].css('id').text.strip.downcase + @contact = Epp::Contact.find_by(code: code) + + if @contact.blank? + epp_errors << { + code: '2303', + msg: t('errors.messages.epp_obj_does_not_exist'), + value: { obj: 'id', val: code } + } + fail CanCan::AccessDenied end - contact + @contact end - def owner?(with_errors = true) - return false unless find_contact - return true if @contact.registrar == current_user.registrar - return false unless with_errors - epp_errors << { code: '2201', msg: t('errors.messages.epp_authorization_error') } - false + # + # Validations + # + def validate_info + @prefix = 'info > info >' + requires 'id' end - def rights? - pw = @ph.try(:[], :authInfo).try(:[], :pw) - - return true if current_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 + def validate_check + @prefix = 'check > check >' + requires 'id' 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) || [])) + def validate_create + @prefix = 'create > create >' + requires( + 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' ) - contact_hash[:disclosure_attributes] = - ContactDisclosure.extract_attributes(params[:parsed_frame]) - - contact_hash + ident = params[:parsed_frame].css('ident') + if ident.present? && ident.text != 'birthday' && ident.attr('cc').blank? + epp_errors << { + code: '2003', + msg: I18n.t('errors.messages.required_attribute_missing', key: 'ident country code missing') + } + end + @prefix = nil + requires 'extension > extdata > legalDocument' end - def ident_type - result = params[: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 + def validate_update + @prefix = 'update > update >' + if element_count('chg') == 0 && element_count('rem') == 0 && element_count('add') == 0 + epp_errors << { + code: '2003', + msg: I18n.t('errors.messages.required_parameter_missing', key: 'add, rem or chg') + } + end + requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end - def validate_params - return true if @ph - epp_errors << { code: '2001', msg: t(:'errors.messages.epp_command_syntax_error') } - false + def validate_delete + @prefix = 'delete > delete >' + requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end end diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 26eeb7eb6..4530fc180 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -1,4 +1,6 @@ class Epp::DomainsController < EppController + skip_authorization_check # TODO: remove it + def create @domain = Epp::EppDomain.new(domain_create_params) @@ -206,7 +208,7 @@ class Epp::DomainsController < EppController return domain if domain.auth_info == params[:parsed_frame].css('authInfo pw').text - if (domain.registrar != current_user.registrar && secure[:secure] == true) && + if (domain.registrar != current_user.registrar) && secure[:secure] == true epp_errors << { code: '2302', msg: I18n.t('errors.messages.domain_exists_but_belongs_to_other_registrar'), diff --git a/app/controllers/epp/errors_controller.rb b/app/controllers/epp/errors_controller.rb index 43b8ee6b1..cefa026a0 100644 --- a/app/controllers/epp/errors_controller.rb +++ b/app/controllers/epp/errors_controller.rb @@ -1,4 +1,6 @@ class Epp::ErrorsController < EppController + skip_authorization_check # TODO: remove it + def error epp_errors << { code: params[:code], msg: params[:msg] } render_epp_response '/epp/error' diff --git a/app/controllers/epp/keyrelays_controller.rb b/app/controllers/epp/keyrelays_controller.rb index f84694e5b..8a9b863d4 100644 --- a/app/controllers/epp/keyrelays_controller.rb +++ b/app/controllers/epp/keyrelays_controller.rb @@ -1,4 +1,6 @@ class Epp::KeyrelaysController < EppController + skip_authorization_check # TODO: remove it + # rubocop: disable Metrics/PerceivedComplexity # rubocop: disable Metrics/CyclomaticComplexity def keyrelay diff --git a/app/controllers/epp/polls_controller.rb b/app/controllers/epp/polls_controller.rb index 3376956e2..2f445abc6 100644 --- a/app/controllers/epp/polls_controller.rb +++ b/app/controllers/epp/polls_controller.rb @@ -1,4 +1,6 @@ class Epp::PollsController < EppController + skip_authorization_check # TODO: remove it + def poll req_poll if params[:parsed_frame].css('poll').first['op'] == 'req' ack_poll if params[:parsed_frame].css('poll').first['op'] == 'ack' @@ -38,6 +40,6 @@ class Epp::PollsController < EppController private def validate_poll - requires_attribute 'poll', 'op', values: %(ack req) + requires_attribute 'poll', 'op', values: %(ack req), allow_blank: true end end diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 53ac68421..6a4696e49 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -1,4 +1,6 @@ class Epp::SessionsController < EppController + skip_authorization_check only: [:hello, :login, :logout] + def hello render_epp_response('greeting') end diff --git a/app/controllers/epp_controller.rb b/app/controllers/epp_controller.rb index 28eff040f..89623117c 100644 --- a/app/controllers/epp_controller.rb +++ b/app/controllers/epp_controller.rb @@ -1,10 +1,23 @@ class EppController < ApplicationController + layout false protect_from_forgery with: :null_session + skip_before_action :verify_authenticity_token + before_action :generate_svtrid before_action :validate_request - layout false helper_method :current_user + rescue_from CanCan::AccessDenied do |_exception| + @errors ||= [] + if @errors.blank? + @errors = [{ + msg: t('errors.messages.epp_authorization_error'), + code: '2201' + }] + end + render_epp_response '/epp/error' + end + def generate_svtrid # rubocop: disable Style/VariableName @svTRID = "ccReg-#{format('%010d', rand(10**10))}" @@ -84,12 +97,19 @@ class EppController < ApplicationController # TODO: Add possibility to pass validations / options in the method def requires(*selectors) + options = selectors.extract_options! + allow_blank = options[:allow_blank] ||= false # allow_blank is false by default + el, missing = nil, nil selectors.each do |selector| full_selector = [@prefix, selector].compact.join(' ') el = params[:parsed_frame].css(full_selector).first - missing = el.nil? + if allow_blank + missing = el.nil? + else + missing = el.present? ? el.text.blank? : true + end epp_errors << { code: '2003', msg: I18n.t('errors.messages.required_parameter_missing', key: full_selector) @@ -105,7 +125,7 @@ class EppController < ApplicationController # requires_attribute 'transfer', 'op', values: %(approve, query, reject) def requires_attribute(element_selector, attribute_selector, options) - element = requires(element_selector) + element = requires(element_selector, allow_blank: options[:allow_blank]) return unless element attribute = element[attribute_selector] diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a6c1a9ff6..ff4f74517 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,6 @@ class SessionsController < Devise::SessionsController + skip_authorization_check + def create # TODO: Create ID Card login here: # this is just testing config diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cc61f56db..0c51ce5a7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -8,4 +8,13 @@ module ApplicationHelper return '' if unstable_env.nil? "background-image: url(#{image_path(unstable_env.to_s + '.png')});" end + + def ident_indicator(contact) + case contact.ident_type + when 'birthday' + "[#{contact.ident_type}]" + else + "[#{contact.ident_country_code} #{contact.ident_type}]" + end + end end diff --git a/app/helpers/whodunnit_helper.rb b/app/helpers/whodunnit_helper.rb deleted file mode 100644 index a1d2999c6..000000000 --- a/app/helpers/whodunnit_helper.rb +++ /dev/null @@ -1,25 +0,0 @@ -module WhodunnitHelper - def link_to_whodunnit(whodunnit) - return nil unless whodunnit - if whodunnit.include?('-ApiUser') - user = ApiUser.find(whodunnit) - return link_to(user.username, admin_epp_user_path(user)) - end - user = AdminUser.find(whodunnit) - return link_to(user.username, admin_user_path(user)) - rescue ActiveRecord::RecordNotFound - return nil - end - - def whodunnit_with_protocol(whodunnit) - return nil unless whodunnit - if whodunnit.include?('-ApiUser') - user = ApiUser.find(whodunnit) - return "#{user.username} (EPP)" - end - user = AdminUser.find(whodunnit) - return user.username - rescue ActiveRecord::RecordNotFound - return nil - end -end diff --git a/app/models/ability.rb b/app/models/ability.rb index 47fc6c209..666ea6685 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,16 +2,31 @@ class Ability include CanCan::Ability def initialize(user) - alias_action :create, :read, :update, :destroy, to: :crud + alias_action :show, :create, :update, :destroy, to: :crud @user = user || AdminUser.new - @user.roles.each { |role| send(role) } if @user.roles - - return if @user.roles || @user.roles.any? + + case @user.class.to_s + when 'AdminUser' + @user.roles.each { |role| send(role) } if @user.roles + when 'ApiUser' + epp + end can :show, :dashboard end + def epp + # Epp::Contact + can(:info, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } + can(:check, Epp::Contact) + can(:create, Epp::Contact) + can(:update, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw } + can(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw } + can(:renew, Epp::Contact) + can(:view_password, Epp::Contact) { |c| c.registrar_id == @user.registrar_id } + end + def user can :show, :dashboard end diff --git a/app/models/api_user.rb b/app/models/api_user.rb index fe0368125..aee1ff203 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -9,10 +9,15 @@ class ApiUser < User validates :username, :password, :registrar, presence: true validates :username, uniqueness: true - before_save :create_crt, if: -> (au) { au.csr_changed? } + # before_save :create_crt, if: -> (au) { au.csr_changed? } attr_accessor :registrar_typeahead + def ability + @ability ||= Ability.new(self) + end + delegate :can?, :cannot?, to: :ability + def registrar_typeahead @registrar_typeahead || registrar || nil end diff --git a/app/models/concerns/version_session.rb b/app/models/concerns/version_session.rb index 50095f2c1..4d455d784 100644 --- a/app/models/concerns/version_session.rb +++ b/app/models/concerns/version_session.rb @@ -5,7 +5,7 @@ module VersionSession before_save :add_session def add_session - self.session = PaperSession.session + self.session = ::PaperSession.session true end end diff --git a/app/models/concerns/versions.rb b/app/models/concerns/versions.rb index d14501e74..8918647f1 100644 --- a/app/models/concerns/versions.rb +++ b/app/models/concerns/versions.rb @@ -28,9 +28,9 @@ module Versions return nil if creator_str.blank? if creator_str =~ /^\d-api-/ - ApiUser.find(creator_str) + ApiUser.find_by(id: creator_str) else - AdminUser.find(creator_str) + AdminUser.find_by(id: creator_str) end end @@ -38,9 +38,9 @@ module Versions return nil if updator_str.blank? if updator_str =~ /^\d-api-/ - ApiUser.find(updator_str) + ApiUser.find_by(id: updator_str) else - AdminUser.find(updator_str) + AdminUser.find_by(id: updator_str) end end diff --git a/app/models/contact.rb b/app/models/contact.rb index 4238924b0..722e4de33 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -1,6 +1,5 @@ class Contact < ActiveRecord::Base include Versions # version/contact_version.rb - include EppErrors has_one :address, dependent: :destroy has_one :disclosure, class_name: 'ContactDisclosure', dependent: :destroy @@ -8,45 +7,42 @@ class Contact < ActiveRecord::Base has_many :domain_contacts has_many :domains, through: :domain_contacts has_many :statuses, class_name: 'ContactStatus' + has_many :legal_documents, as: :documentable belongs_to :registrar - accepts_nested_attributes_for :address, :disclosure + accepts_nested_attributes_for :address, :disclosure, :legal_documents validates :name, :phone, :email, :ident, :address, :registrar, :ident_type, presence: true # Phone nr validation is very minimam in order to support legacy requirements validates :phone, format: /\+[0-9]{1,3}\.[0-9]{1,14}?/ validates :email, format: /@/ - validates :ident, format: /\d{4}-\d{2}-\d{2}/, if: proc { |c| c.ident_type == 'birthday' } - - validate :ident_must_be_valid - + 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(bic priv).include? c.ident_type } validates :code, uniqueness: { message: :epp_id_taken } + validate :ident_valid_format? - delegate :city, to: :address # , prefix: true - delegate :street, to: :address # , prefix: true - delegate :zip, to: :address # , prefix: true + delegate :street, to: :address + delegate :city, to: :address + delegate :zip, to: :address + delegate :state, to: :address + delegate :country_code, to: :address + delegate :country, to: :address - # callbacks - # TODO: remove old - # after_commit :domains_snapshot - # after_update :domains_snapshot - # after_destroy :domains_snapshot + before_validation :set_ident_country_code before_create :generate_code before_create :generate_auth_info after_create :ensure_disclosure - # scopes scope :current_registrars, ->(id) { where(registrar_id: id) } - IDENT_TYPE_ICO = 'ico' + IDENT_TYPE_BIC = 'bic' IDENT_TYPES = [ - IDENT_TYPE_ICO, # Company registry code (or similar) - 'bic', # Business registry code + IDENT_TYPE_BIC, # Company registry code (or similar) 'priv', # National idendtification number - 'op', # Estonian ID, depricated - 'passport', # Passport number, depricated 'birthday' # Birthday date ] @@ -54,45 +50,53 @@ class Contact < ActiveRecord::Base CONTACT_TYPE_ADMIN = 'admin' CONTACT_TYPES = [CONTACT_TYPE_TECH, CONTACT_TYPE_ADMIN] - def ident_must_be_valid - # TODO: Ident can also be passport number or company registry code. - # so have to make changes to validations (and doc/schema) accordingly - return true unless ident.present? && ident_type.present? && ident_type == 'op' - code = Isikukood.new(ident) - errors.add(:ident, 'bad format') unless code.valid? + class << self + def search_by_query(query) + res = search(code_cont: query).result + res.reduce([]) { |o, v| o << { id: v[:id], display_key: "#{v.name} (#{v.code})" } } + end + + def check_availability(codes) + codes = [codes] if codes.is_a?(String) + + res = [] + codes.each do |x| + if Contact.find_by(code: x) + res << { code: x, avail: 0, reason: 'in use' } + else + res << { code: x, avail: 1 } + end + end + + res + end + end + + def to_s + name + end + + def ident_valid_format? + case ident_type + when 'priv' + case ident_country_code + when 'EE' + code = Isikukood.new(ident) + errors.add(:ident, :invalid_EE_identity_format) unless code.valid? + end + end end def ensure_disclosure create_disclosure! unless disclosure end - # TODO: remove old - # def domains_snapshot - # (domains + domains_owned).uniq.each do |domain| - # next unless domain.is_a?(Domain) - # # next if domain.versions.last == domain.create_snapshot - # domain.create_version # Method from paper_trail - # end - # end - - def juridical? - ident_type == IDENT_TYPE_ICO + def bic? + ident_type == IDENT_TYPE_BIC end - def citizen? - ident_type != IDENT_TYPE_ICO - end - - def cr_id - # created_by ? created_by.username : nil - end - - def up_id - # updated_by ? updated_by.username : nil - end - - def auth_info_matches(pw) - auth_info == pw + def priv? + ident_type != IDENT_TYPE_BIC end # generate random id for contact @@ -114,6 +118,8 @@ class Contact < ActiveRecord::Base false end + # TODO: refactor, it should not allow to destroy with normal destroy, + # no need separate method # should use only in transaction def destroy_and_clean if relations_with_domain? @@ -123,75 +129,13 @@ class Contact < ActiveRecord::Base destroy end - def epp_code_map # rubocop:disable Metrics/MethodLength - { - '2302' => [ # Object exists - [:code, :epp_id_taken] - ], - '2305' => [ # Association exists - [:domains, :exist] - ], - '2005' => [ # Value syntax error - [:phone, :invalid], - [:email, :invalid], - [:ident, :invalid] - ] - } - end - - def to_s - name - end - - # TODO: remove old - # for archiving - # def snapshot - # { - # name: name, - # phone: phone, - # code: code, - # ident: ident, - # email: email - # } - # end - - class << self - # non-EPP - - # EPP - def extract_attributes(ph, _type = :create) - ph[:postalInfo] = ph[:postalInfo].first if ph[:postalInfo].is_a?(Array) - contact_hash = { - phone: ph[:voice], - ident: ph[:ident], - ident_type: ph[:ident_type], - email: ph[:email], - fax: ph[:fax], - name: ph[:postalInfo].try(:[], :name), - org_name: ph[:postalInfo].try(:[], :org) - } - # contact_hash[:auth_info] = ph[:authInfo][:pw] if type == :create - contact_hash.delete_if { |_k, v| v.nil? } - end - - def check_availability(codes) - codes = [codes] if codes.is_a?(String) - - res = [] - codes.each do |x| - if Contact.find_by(code: x) - res << { code: x, avail: 0, reason: 'in use' } - else - res << { code: x, avail: 1 } - end - end - - res - end - - def search_by_query(query) - res = search(code_cont: query).result - res.reduce([]) { |o, v| o << { id: v[:id], display_key: "#{v.name} (#{v.code})" } } + def set_ident_country_code + return true unless ident_country_code_changed? && ident_country_code.present? + code = Country.new(ident_country_code) + if code + self.ident_country_code = code.alpha2 + else + errors.add(:ident_country_code, 'is not following ISO_3166-1 alpha 2 format') end end end diff --git a/app/models/domain.rb b/app/models/domain.rb index ee70421f0..2d0a96673 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -20,8 +20,6 @@ class Domain < ActiveRecord::Base -> { where(domain_contacts: { contact_type: DomainContact::ADMIN }) }, through: :domain_contacts, source: :contact - # TODO: remove old - # has_many :nameservers, dependent: :delete_all, after_add: :track_nameserver_add has_many :nameservers, dependent: :delete_all accepts_nested_attributes_for :nameservers, allow_destroy: true, @@ -42,11 +40,11 @@ class Domain < ActiveRecord::Base has_many :legal_documents, as: :documentable - delegate :code, to: :owner_contact, prefix: true + delegate :code, to: :owner_contact, prefix: true delegate :email, to: :owner_contact, prefix: true delegate :ident, to: :owner_contact, prefix: true delegate :phone, to: :owner_contact, prefix: true - delegate :name, to: :registrar, prefix: true + delegate :name, to: :registrar, prefix: true before_create :generate_auth_info before_create :set_validity_dates @@ -117,11 +115,6 @@ class Domain < ActiveRecord::Base attr_accessor :owner_contact_typeahead, :update_me - # TODO: remove old - # archiving - # if proc works only on changes on domain sadly - # has_paper_trail class_name: 'DomainVersion', meta: { snapshot: :create_snapshot }, if: proc(&:new_version) - def tech_domain_contacts domain_contacts.select { |x| x.contact_type == DomainContact::TECH } end @@ -130,52 +123,6 @@ class Domain < ActiveRecord::Base domain_contacts.select { |x| x.contact_type == DomainContact::ADMIN } end - # TODO: remove old - # def new_version - # return false if versions.try(:last).try(:snapshot) == create_snapshot - # true - # end - - # TODO: remove old - # def create_version - # return true unless PaperTrail.enabled? - # return true unless valid? - # touch_with_version if new_version - # end - - # TODO: remove old - # def track_nameserver_add(_nameserver) - # return true if versions.count == 0 - # return true unless valid? && new_version - - # touch_with_version - # end - - # TODO: remove old - # def create_snapshot - # oc = owner_contact.snapshot if owner_contact.is_a?(Contact) - # { - # owner_contact: oc, - # tech_contacts: tech_contacts.map(&:snapshot), - # admin_contacts: admin_contacts.map(&:snapshot), - # nameservers: nameservers.map(&:snapshot), - # domain: make_snapshot - # }.to_yaml - # end - - # TODO: remove old - # def make_snapshot - # { - # name: name, - # status: status, - # period: period, - # period_unit: period_unit, - # registrar_id: registrar.try(:id), - # valid_to: valid_to, - # valid_from: valid_from - # } - # end - def name=(value) value.strip! value.downcase! @@ -264,7 +211,7 @@ class Domain < ActiveRecord::Base attach_contact(DomainContact::TECH, owner_contact) end - return unless admin_domain_contacts.count.zero? && owner_contact.citizen? + return unless admin_domain_contacts.count.zero? && owner_contact.priv? attach_contact(DomainContact::ADMIN, owner_contact) end @@ -304,36 +251,36 @@ class Domain < ActiveRecord::Base # rubocop:disable Metrics/MethodLength def update_whois_body self.whois_body = <<-EOS - This Whois Server contains information on - Estonian Top Level Domain ee TLD + This Whois Server contains information on + Estonian Top Level Domain ee TLD - domain: #{name} - registrar: #{registrar} - status: - registered: - changed: #{updated_at.to_s(:db)} - expire: - outzone: - delete: + domain: #{name} + registrar: #{registrar} + status: + registered: + changed: #{updated_at.to_s(:db)} + expire: + outzone: + delete: - contact - name: - e-mail: - registrar: - created: + contact + name: + e-mail: + registrar: + created: - contact: + contact: - nsset: - nserver: + nsset: + nserver: - registrar: - org: - url: - phone: - address: - created: - changed: + registrar: + org: + url: + phone: + address: + created: + changed: EOS end # rubocop:enabled Metrics/MethodLength diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb new file mode 100644 index 000000000..276b490d6 --- /dev/null +++ b/app/models/epp/contact.rb @@ -0,0 +1,93 @@ +# rubocop: disable Metrics/ClassLength +class Epp::Contact < Contact + include EppErrors + + # disable STI, there is type column present + self.inheritance_column = :sti_disabled + + # temp fix + has_many :legal_documents, as: :documentable + + class << self + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity + # rubocop: disable Metrics/MethodLength + def attrs_from(frame) + f = frame + at = {}.with_indifferent_access + at[:name] = f.css('postalInfo name').text if f.css('postalInfo name').present? + at[:org_name] = f.css('postalInfo org').text if f.css('postalInfo org').present? + at[:email] = f.css('email').text if f.css('email').present? + at[:fax] = f.css('fax').text if f.css('fax').present? + at[:phone] = f.css('voice').text if f.css('voice').present? + at[:auth_info] = f.css('authInfo pw').text if f.css('authInfo pw').present? + + if f.css('ident').present? && f.css('ident').attr('type').present? + at[:ident] = f.css('ident').text + at[:ident_type] = f.css('ident').attr('type').try(:text) + at[:ident_country_code] = f.css('ident').attr('cc').try(:text) + end + + at[:address_attributes] = {}.with_indifferent_access + sat = at[:address_attributes] + sat[:city] = f.css('postalInfo addr city').text if f.css('postalInfo addr city').present? + sat[:zip] = f.css('postalInfo addr pc').text if f.css('postalInfo addr pc').present? + sat[:street] = f.css('postalInfo addr street').text if f.css('postalInfo addr street').present? + sat[:state] = f.css('postalInfo addr sp').text if f.css('postalInfo addr sp').present? + sat[:country_code] = f.css('postalInfo addr cc').text if f.css('postalInfo addr cc').present? + at.delete(:address_attributes) if at[:address_attributes].blank? + + legal_frame = f.css('legalDocument').first + if legal_frame.present? + at[:legal_documents_attributes] = legal_document_attrs(legal_frame) + end + + at + end + # rubocop: enable Metrics/MethodLength + # rubocop: enable Metrics/PerceivedComplexity + # rubocop: enable Metrics/CyclomaticComplexity + + def new(frame) + return super if frame.blank? + super(attrs_from(frame)) + end + + def legal_document_attrs(legal_frame) + attrs = {}.with_indifferent_access + attrs[0] = {}.with_indifferent_access + attrs[0][:body] = legal_frame.text + attrs[0][:document_type] = legal_frame['type'] + attrs + end + end + + def epp_code_map # rubocop:disable Metrics/MethodLength + { + '2302' => [ # Object exists + [:code, :epp_id_taken] + ], + '2305' => [ # Association exists + [:domains, :exist] + ], + '2005' => [ # Value syntax error + [:phone, :invalid], + [:email, :invalid], + [:ident, :invalid], + [:ident, :invalid_EE_identity_format], + [:ident, :invalid_birthday_format] + ] + } + end + + def update_attributes(frame) + return super if frame.blank? + at = {}.with_indifferent_access + at.deep_merge!(self.class.attrs_from(frame.css('chg'))) + legal_frame = frame.css('legalDocument').first + at[:legal_documents_attributes] = self.class.legal_document_attrs(legal_frame) + + super(at) + end +end +# rubocop: enable Metrics/ClassLength diff --git a/app/models/epp/epp_domain.rb b/app/models/epp/epp_domain.rb index 7332b423f..3847d61ea 100644 --- a/app/models/epp/epp_domain.rb +++ b/app/models/epp/epp_domain.rb @@ -129,7 +129,7 @@ class Epp::EppDomain < Domain next end - if k == :admin && contact.juridical? + if k == :admin && contact.bic? add_epp_error('2306', 'contact', x[:contact], [:domain_contacts, :admin_contact_can_be_only_citizen]) next end diff --git a/app/views/admin/api_users/show.haml b/app/views/admin/api_users/show.haml index 63ffc3952..f8f09a701 100644 --- a/app/views/admin/api_users/show.haml +++ b/app/views/admin/api_users/show.haml @@ -43,7 +43,7 @@ %dd - %dt= t('crt') - - if @api_user.csr - %dd= link_to(t('download'), download_crt_admin_api_user_path) - - else - %dd - + / - if @api_user.csr + / %dd= link_to(t('download'), download_crt_admin_api_user_path) + / - else + / %dd - diff --git a/app/views/admin/contacts/index.haml b/app/views/admin/contacts/index.haml index 7c27cf646..3f437d62d 100644 --- a/app/views/admin/contacts/index.haml +++ b/app/views/admin/contacts/index.haml @@ -22,20 +22,20 @@ %thead %tr %th{class: 'col-xs-2'} - = sort_link(@q, 'name', t('name')) + = sort_link(@q, 'name', t(:name)) %th{class: 'col-xs-2'} - = sort_link(@q, 'code', t('code')) + = sort_link(@q, 'ident', t(:identity)) %th{class: 'col-xs-2'} - = sort_link(@q, 'ident', t('identity_code')) + = sort_link(@q, 'email', t(:email)) %th{class: 'col-xs-2'} - = sort_link(@q, 'email', t('email')) + = sort_link(@q, 'code', t(:code)) %tbody - - @contacts.each do |x| + - @contacts.each do |contact| %tr - %td= link_to(x, admin_contact_path(x)) - %td= x.code - %td= x.ident - %td= x.email + %td= link_to(contact, admin_contact_path(contact)) + %td= "#{contact.ident} #{ident_indicator(contact)}" + %td= contact.email + %td= contact.code .row .col-md-12 = paginate @contacts diff --git a/app/views/admin/contacts/partials/_address.haml b/app/views/admin/contacts/partials/_address.haml index 1d27ba7bd..aecba9a70 100644 --- a/app/views/admin/contacts/partials/_address.haml +++ b/app/views/admin/contacts/partials/_address.haml @@ -1,28 +1,24 @@ .panel.panel-default .panel-heading - %h3.panel-title= t('address') + %h3.panel-title= t(:address) .panel-body - unless @contact.address.nil? %dl.dl-horizontal - %dt= t('country') - %dd= @contact.address.country + - if @contact.bic? + %dt= t(:org_name) + %dd= @contact.org_name - %dt= t('city') - %dd= @contact.address.city + %dt= t(:street) + %dd= @contact.street - %dt= t('street') - %dd= @contact.address.street + %dt= t(:city) + %dd= @contact.city - - if @contact.address.street2 - %dt= t('street') - %dd= @contact.address.street2 + %dt= t(:zip) + %dd= @contact.zip - - if @contact.address.street3 - %dt= t('street') - %dd= @contact.address.street3 - - - - %dt= t('zip') - %dd= @contact.address.zip + %dt= t(:state) + %dd= @contact.state + %dt= t(:country) + %dd= @contact.country diff --git a/app/views/admin/contacts/partials/_general.haml b/app/views/admin/contacts/partials/_general.haml index d5cc42044..2911ab783 100644 --- a/app/views/admin/contacts/partials/_general.haml +++ b/app/views/admin/contacts/partials/_general.haml @@ -3,30 +3,25 @@ %h3.panel-title= t('general') .panel-body %dl.dl-horizontal - %dt= t('name') - %dd= @contact.name + %dt= t(:ident) + %dd= "#{@contact.ident} #{ident_indicator(@contact)}" - %dt= t('org_name') - %dd= @contact.org_name + %br - %dt= t('code') - %dd= @contact.code - - %dt= t('ident') - %dd= @contact.ident - - %dt= t('ident_type') - %dd= @contact.ident_type - - %dt= t('email') + %dt= t(:email) %dd= @contact.email - %dt= t('phone') + %dt= t(:phone) %dd= @contact.phone - if @contact.fax - %dt= t('fax') + %dt= t(:fax) %dd= @contact.fax + %br + + %dt= t(:code) + %dd= @contact.code + %dt= t('password') %dd= @contact.auth_info diff --git a/app/views/admin/contacts/show.haml b/app/views/admin/contacts/show.haml index d5620bb7c..dba7dd39a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -1,10 +1,13 @@ .row .col-sm-12 %h2.text-center-xs - = "#{t('contact_details')}" + = @contact.name %hr .row .col-md-6= render 'admin/contacts/partials/general' .col-md-6= render 'admin/contacts/partials/address' .row .col-md-12= render 'admin/contacts/partials/domains' +.row + .col-md-12 + = render 'admin/domains/partials/legal_documents', legal_documents: @contact.legal_documents diff --git a/app/views/admin/domains/partials/_legal_documents.haml b/app/views/admin/domains/partials/_legal_documents.haml index b9fe5144e..5a25ae325 100644 --- a/app/views/admin/domains/partials/_legal_documents.haml +++ b/app/views/admin/domains/partials/_legal_documents.haml @@ -8,7 +8,7 @@ %th{class: 'col-xs-8'}= t('created_at') %th{class: 'col-xs-4'}= t('type') %tbody - - @domain.legal_documents.each do |x| + - legal_documents.each do |x| %tr %td= link_to(x.created_at, [:admin, x]) %td= x.document_type diff --git a/app/views/admin/domains/show.haml b/app/views/admin/domains/show.haml index bbb790737..215ade8b1 100644 --- a/app/views/admin/domains/show.haml +++ b/app/views/admin/domains/show.haml @@ -24,4 +24,5 @@ .row .col-md-12= render 'admin/domains/partials/keyrelays' .row - .col-md-12= render 'admin/domains/partials/legal_documents' + .col-md-12 + = render 'admin/domains/partials/legal_documents', legal_documents: @domain.legal_documents diff --git a/app/views/epp/contacts/_postal_info.xml.builder b/app/views/epp/contacts/_postal_info.xml.builder index f9c6c5ee9..f84f177c9 100644 --- a/app/views/epp/contacts/_postal_info.xml.builder +++ b/app/views/epp/contacts/_postal_info.xml.builder @@ -1,13 +1,13 @@ -address = @contact.address xml.tag!('contact:postalInfo', type: 'int') do - xml.tag!('contact:name', @contact.name) if @disclosure.try(:[], :name) || @owner - xml.tag!('contact:org', @contact.org_name) if @disclosure.try(:[], :org_name) || @owner - if @disclosure.try(:addr) || @owner + xml.tag!('contact:name', @contact.name) #if @disclosure.try(:[], :name) || @owner + xml.tag!('contact:org', @contact.org_name) #if @disclosure.try(:[], :org_name) || @owner + # if @disclosure.try(:addr) || @owner xml.tag!('contact:addr') do - xml.tag!('contact:street', address.street) if address - xml.tag!('contact:cc', address.country_code) unless address.country_code.nil? - xml.tag!('contact:city', address.city) if address + xml.tag!('contact:street', @contact.street) + xml.tag!('contact:city', @contact.city) + xml.tag!('contact:pc', @contact.zip) + xml.tag!('contact:sp', @contact.state) + xml.tag!('contact:cc', @contact.country_code) end - end + # end end - diff --git a/app/views/epp/contacts/check.xml.builder b/app/views/epp/contacts/check.xml.builder index dab344196..4df3597e3 100644 --- a/app/views/epp/contacts/check.xml.builder +++ b/app/views/epp/contacts/check.xml.builder @@ -6,11 +6,10 @@ xml.epp_head do xml.resData do xml.tag!('contact:chkData', 'xmlns:contact' => 'urn:ietf:params:xml:ns:contact-1.0') do - #xml.tag!('contact:id', @contact.code) - @contacts.each do |contact| + @results.each do |result| xml.tag!('contact:cd') do - xml.tag! "contact:id", contact[:code], avail: contact[:avail] - xml.tag!('contact:reason', contact[:reason]) unless contact[:avail] == 1 + xml.tag! "contact:id", result[:code], avail: result[:avail] + xml.tag!('contact:reason', result[:reason]) unless result[:avail] == 1 end end end diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 0f4560041..ffe884485 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -8,9 +8,9 @@ xml.epp_head do xml.tag!('contact:chkData', 'xmlns:contact' => 'urn:ietf:params:xml:ns:contact-1.0') do xml.tag!('contact:id', @contact.code) xml << render('/epp/contacts/postal_info') - xml.tag!('contact:voice', @contact.phone) if @disclosure.try(:phone) || @owner - xml.tag!('contact:fax', @contact.fax) if @disclosure.try(:fax) || @owner - xml.tag!('contact:email', @contact.email) if @disclosure.try(:email) || @owner + xml.tag!('contact:voice', @contact.phone) #if @disclosure.try(:phone) || @owner + xml.tag!('contact:fax', @contact.fax) #if @disclosure.try(:fax) || @owner + xml.tag!('contact:email', @contact.email) #if @disclosure.try(:email) || @owner xml.tag!('contact:clID', @contact.registrar.try(:name)) xml.tag!('contact:crID', @contact.creator.try(:registrar)) xml.tag!('contact:crDate', @contact.created_at) @@ -19,17 +19,16 @@ xml.epp_head do xml.tag!('contact:upDate', @contact.updated_at) end xml.tag!('contact:ident', @contact.ident, type: @contact.ident_type) - xml.tag!('contact:trDate', '123') if false - if @owner + # xml.tag!('contact:trDate', '123') if false + if can? :view_password, @contact xml.tag!('contact:authInfo') do - xml.tag!('contact:pw', @contact.auth_info) # Doc says we have to return this but is it necessary? + xml.tag!('contact:pw', @contact.auth_info) end end - # statuses - @contact.statuses.each do |cs| - xml.tag!('contact:status', s: cs.value) + @contact.statuses.each do |status| + xml.tag!('contact:status', s: status.value) end - xml << render('/epp/contacts/disclosure_policy') + # xml << render('/epp/contacts/disclosure_policy') end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 5ab4ccef9..3adea8723 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,24 +1,3 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. - en: views: pagination: @@ -52,7 +31,6 @@ en: activerecord: errors: models: - contact: attributes: code: @@ -67,6 +45,8 @@ en: invalid: "Email is invalid" ident: blank: "Required parameter missing - ident" + invalid_EE_identity_format: "Ident not in valid Estonian identity format." + invalid_birthday_format: "Ident not in valid birthady format, should be YYYY-MM-DD" domains: exist: 'Object association prohibits operation' @@ -258,7 +238,7 @@ en: invalid_type: 'PostalInfo type is invalid' unimplemented_command: 'Unimplemented command' domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar' - + required_attribute_missing: Required attributes missing code: 'Code' value: 'Value' @@ -321,7 +301,7 @@ en: domain_status_prohibits_deleting: 'Domain status prohibits deleting' domain_deleted: 'Domain deleted!' failed_to_delete_domain: 'Failed to delete domain!' - email: 'Email' + email: 'E-mail' fax: 'Fax' contact_details: 'Contact details' ident: 'Ident' @@ -330,8 +310,8 @@ en: country: 'Country' city: 'City' street: 'Street' - zip: 'Zip' - org_name: 'Organisation name' + zip: 'Postcode' + org_name: 'Org name' failed_to_add_domain: 'Failed to add domain!' domain_added: 'Domain added!' new_contact: 'New contact' @@ -501,3 +481,4 @@ en: address_help: 'Street name, house no - apartment no, city, county, country, zip' download: 'Download' failed_to_create_certificate: 'Failed to create certificate!' + contact_code: Contact code diff --git a/db/migrate/20150212125339_add_state_to_address.rb b/db/migrate/20150212125339_add_state_to_address.rb new file mode 100644 index 000000000..a57412ce6 --- /dev/null +++ b/db/migrate/20150212125339_add_state_to_address.rb @@ -0,0 +1,5 @@ +class AddStateToAddress < ActiveRecord::Migration + def change + add_column :addresses, :state, :string + end +end diff --git a/db/migrate/20150217133755_add_country_code_ident.rb b/db/migrate/20150217133755_add_country_code_ident.rb new file mode 100644 index 000000000..a45abd2aa --- /dev/null +++ b/db/migrate/20150217133755_add_country_code_ident.rb @@ -0,0 +1,5 @@ +class AddCountryCodeIdent < ActiveRecord::Migration + def change + add_column :contacts, :ident_country_code, :string + end +end diff --git a/db/migrate/20150217133937_add_index_for_contact_code.rb b/db/migrate/20150217133937_add_index_for_contact_code.rb new file mode 100644 index 000000000..ff5a4a94b --- /dev/null +++ b/db/migrate/20150217133937_add_index_for_contact_code.rb @@ -0,0 +1,5 @@ +class AddIndexForContactCode < ActiveRecord::Migration + def change + add_index :contacts, :code + end +end diff --git a/db/schema.rb b/db/schema.rb index 614fe9ae6..f245f12ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150213104014) do +ActiveRecord::Schema.define(version: 20150217133937) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -29,6 +29,7 @@ ActiveRecord::Schema.define(version: 20150213104014) do t.string "creator_str" t.string "updator_str" t.string "country_code" + t.string "state" end create_table "api_users", force: :cascade do |t| @@ -95,8 +96,11 @@ ActiveRecord::Schema.define(version: 20150213104014) do t.integer "registrar_id" t.string "creator_str" t.string "updator_str" + t.string "ident_country_code" end + add_index "contacts", ["code"], name: "index_contacts_on_code", using: :btree + create_table "countries", force: :cascade do |t| t.string "iso" t.string "name" diff --git a/db/seeds.rb b/db/seeds.rb index ec49d6dbd..15d20166b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -4,7 +4,10 @@ registrar1 = Registrar.where( name: 'Registrar First AS', reg_no: '10300220', - address: 'Pärnu mnt 2, Tallinna linn, Harju maakond, 11415', + street: 'Pärnu mnt 2', + city: 'Tallinn', + state: 'Harju maakond', + zip: '11415', email: 'registrar1@example.com', country_code: 'EE' ).first_or_create! @@ -19,7 +22,10 @@ ApiUser.where( registrar2 = Registrar.where( name: 'Registrar Second AS', reg_no: '10529229', - address: 'Vabaduse pst 32, 11316 Tallinn', + street: 'Vabaduse pst 32', + city: 'Tallinn', + state: 'Harju maakond', + zip: '11315', email: 'registrar2@example.com', country_code: 'EE' ).first_or_create! diff --git a/doc/epp/contact.md b/doc/epp/contact.md index 1117d0c86..7bfe35468 100644 --- a/doc/epp/contact.md +++ b/doc/epp/contact.md @@ -30,12 +30,10 @@ Contact Mapping protocol short version: "priv" # National idendtification number "birthday" # Birthday date in format in DD-MM-YYYY 1 - 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" - 1 Base64 encoded document + 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" + 1 Base64 encoded document Attribute: type="pdf/bdoc/ddoc/zip/rar/gz/tar/7z" - - [EXAMPLE REQUEST AND RESPONSE](/doc/epp-examples.md#epp-contact-with-valid-user-create-command-successfully-creates-a-contact) ### Contact update @@ -65,9 +63,9 @@ Contact Mapping protocol short version: 0-1 Required if registrar is not the owner of the contact. 1 Contact password. Attribute: roid="String" 0-1 - 0-1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" - 0-1 Base64 encoded document. - Attribute: type="pdf/bdoc/ddoc/zip/rar/gz/tar/7z" + 0-1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" + 0-1 Base64 encoded document. + Attribute: type="pdf/bdoc/ddoc/zip/rar/gz/tar/7z" [EXAMPLE REQUEST AND RESPONSE](/doc/epp-examples.md#epp-contact-with-valid-user-update-command-is-succesful) @@ -82,10 +80,10 @@ Contact Mapping protocol short version: 0-1 Required if registrar is not the owner of the contact. 1 Contact password. Attribute: roid="String" 1 - 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" - 1 Base64 encoded document. + 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" + 1 Base64 encoded document. Attribute: type="pdf/bdoc/ddoc/zip/rar/gz/tar/7z" - 0-1 Client transaction id + 0-1 Client transaction id [EXAMPLE REQUEST AND RESPONSE](/doc/epp-examples.md#epp-contact-with-valid-user-delete-command-deletes-contact) diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 62de51c5a..a025197c6 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -1,451 +1,425 @@ -# require 'rails_helper' - -# describe 'EPP Contact', epp: true do -# before :all do -# create_settings -# create_disclosure_settings -# @registrar1 = Fabricate(:registrar1) -# @registrar2 = Fabricate(:registrar2) -# @epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - -# Fabricate(:api_user, username: 'registrar1', registrar: @registrar1) -# Fabricate(:api_user, username: 'registrar2', registrar: @registrar2) - -# login_as :registrar1 - -# Contact.skip_callback(:create, :before, :generate_code) -# Contact.skip_callback(:create, :before, :generate_auth_info) -# end - -# after :all do -# Contact.set_callback(:create, :before, :generate_code) -# Contact.set_callback(:create, :before, :generate_auth_info) -# end - -# context 'with valid user' do -# context 'create command' do -# it 'fails if request xml is missing' do -# xml = @epp_xml.create -# response = epp_plain_request(xml, :xml) -# response[:results][0][:msg].should == 'Command syntax error' -# response[:results][0][:result_code].should == '2001' - -# response[:results].count.should == 1 -# end - -# it 'fails if request xml is missing' do -# xml = @epp_xml.create( -# postalInfo: { addr: { value: nil } } -# ) -# response = epp_plain_request(xml, :xml) -# response[:results][0][:msg].should == 'Required parameter missing: name' -# response[:results][1][:msg].should == 'Required parameter missing: city' -# response[:results][2][:msg].should == 'Required parameter missing: cc' -# response[:results][3][:msg].should == 'Required parameter missing: ident' -# response[:results][4][:msg].should == 'Required parameter missing: voice' -# response[:results][5][:msg].should == 'Required parameter missing: email' - -# response[:results][0][:result_code].should == '2003' -# response[:results][1][:result_code].should == '2003' -# response[:results][2][:result_code].should == '2003' -# response[:results][3][:result_code].should == '2003' -# response[:results][4][:result_code].should == '2003' -# response[:results][5][:result_code].should == '2003' - -# response[:results].count.should == 6 -# end - -# it 'successfully saves ident type' do -# xml = { ident: { value: '1990-22-12', attrs: { type: 'birthday' } } } -# epp_plain_request(create_contact_xml(xml), :xml) - -# Contact.last.ident_type.should == 'birthday' -# end - -# it 'successfully creates a contact' do -# response = epp_plain_request(create_contact_xml, :xml) - -# response[:msg].should == 'Command completed successfully' -# response[:result_code].should == '1000' - -# @contact = Contact.last - -# @contact.registrar.should == @registrar1 -# # registrar1.api_users.should include(@contact.created_by) -# # @contact.updated_by_id.should == nil -# @contact.ident.should == '37605030299' -# @contact.address.street.should == '123 Example' - -# log = ApiLog::EppLog.last -# log.request_command.should == 'create' -# log.request_object.should == 'contact' -# log.request_successful.should == true -# log.api_user_name.should == '1-api-registrar1' -# log.api_user_registrar.should == 'registrar1' -# end - -# it 'successfully adds registrar' do -# response = epp_plain_request(create_contact_xml, :xml) - -# response[:msg].should == 'Command completed successfully' -# response[:result_code].should == '1000' - -# Contact.last.registrar.should == @registrar1 -# end - -# it 'returns result data upon success' do -# response = epp_plain_request(create_contact_xml, :xml) - -# response[:msg].should == 'Command completed successfully' -# response[:result_code].should == '1000' - -# id = response[:parsed].css('resData creData id').first -# cr_date = response[:parsed].css('resData creData crDate').first - -# id.text.length.should == 8 -# # 5 seconds for what-ever weird lag reasons might happen -# cr_date.text.to_time.should be_within(5).of(Time.now) -# end - -# it 'creates disclosure data' do -# xml = { -# disclose: { value: { -# voice: { value: '' }, -# addr: { value: '' }, -# name: { value: '' }, -# org_name: { value: '' }, -# email: { value: '' }, -# fax: { value: '' } -# }, attrs: { flag: '1' } -# } -# } - -# response = epp_plain_request(create_contact_xml(xml), :xml) -# response[:result_code].should == '1000' - -# @contact = Contact.last -# @contact.disclosure.name.should == true -# @contact.disclosure.org_name.should == true -# @contact.disclosure.phone.should == true -# @contact.disclosure.fax.should == true -# @contact.disclosure.email.should == true -# @contact.disclosure.address.should == true -# end - -# it 'creates disclosure data merging with defaults' do -# xml = { -# disclose: { value: { -# voice: { value: '' }, -# addr: { value: '' } -# }, attrs: { flag: '1' } -# } -# } - -# response = epp_plain_request(create_contact_xml(xml), :xml) -# response[:result_code].should == '1000' - -# @contact = Contact.last -# @contact.disclosure.name.should == nil -# @contact.disclosure.org_name.should == nil -# @contact.disclosure.phone.should == true -# @contact.disclosure.fax.should == nil -# @contact.disclosure.email.should == nil -# @contact.disclosure.address.should == true -# end -# end - -# context 'update command' do -# before :all do -# @contact = -# Fabricate( -# :contact, -# # created_by_id: 1, -# registrar: @registrar1, -# email: 'not_updated@test.test', -# code: 'sh8013', -# auth_info: 'password' -# ) -# end - -# it 'fails if request is invalid' do -# xml = @epp_xml.update -# response = epp_plain_request(xml, :xml) # epp_request('contacts/update_missing_attr.xml') - -# response[:results][0][:result_code].should == '2003' -# response[:results][0][:msg].should == 'Required parameter missing: add, rem or chg' -# response[:results][1][:result_code].should == '2003' -# response[:results][1][:msg].should == 'Required parameter missing: id' -# response[:results].count.should == 2 -# end - -# it 'fails with wrong authentication info' do -# login_as :registrar2 do -# response = epp_plain_request(update_contact_xml({ id: { value: 'sh8013' } }), :xml) -# expect(response[:msg]).to eq('Authorization error') -# expect(response[:result_code]).to eq('2201') -# end -# end - -# it 'is succesful' do -# response = epp_plain_request(update_contact_xml({ id: { value: 'sh8013' } }), :xml) - -# response[:msg].should == 'Command completed successfully' -# @contact.reload -# @contact.name.should == 'John Doe Edited' -# @contact.email.should == 'edited@example.example' -# end - -# it 'returns phone and email error' do -# xml = { -# id: { value: 'sh8013' }, -# chg: { -# voice: { value: '123213' }, -# email: { value: 'aaa' } -# } -# } - -# response = epp_plain_request(update_contact_xml(xml), :xml) - -# response[:results][0][:msg].should == 'Phone nr is invalid' -# response[:results][0][:result_code].should == '2005' - -# response[:results][1][:msg].should == 'Email is invalid' -# response[:results][1][:result_code].should == '2005' -# end - -# it 'updates disclosure items' do -# Fabricate( -# :contact, -# code: 'sh8013disclosure', -# auth_info: '2fooBAR', -# registrar: @registrar1, -# # created_by_id: ApiUser.first.id, -# disclosure: Fabricate(:contact_disclosure, phone: true, email: true)) - -# xml = { -# id: { value: 'sh8013disclosure' }, -# authInfo: { pw: { value: '2fooBAR' } } -# } -# @response = epp_plain_request(update_contact_xml(xml), :xml) - -# @response[:results][0][:msg].should == 'Command completed successfully' -# @response[:results][0][:result_code].should == '1000' - -# Contact.last.disclosure.phone.should == false -# Contact.last.disclosure.email.should == false -# end -# end - -# context 'delete command' do -# it 'fails if request is invalid' do -# xml = @epp_xml.delete({ uid: { value: '23123' } }) -# response = epp_plain_request(xml, :xml) - -# response[:results][0][:msg].should == 'Required parameter missing: id' -# response[:results][0][:result_code].should == '2003' -# response[:results].count.should == 1 -# end - -# it 'deletes contact' do -# @contact_deleted = -# # Fabricate(:contact, code: 'dwa1234', created_by_id: ApiUser.first.id, registrar: registrar1) -# Fabricate(:contact, code: 'dwa1234', registrar: @registrar1) - -# response = epp_plain_request(delete_contact_xml({ id: { value: 'dwa1234' } }), :xml) -# response[:msg].should == 'Command completed successfully' -# response[:result_code].should == '1000' -# response[:clTRID].should == 'ABC-12345' - -# Contact.find_by_id(@contact_deleted.id).should == nil -# end - -# it 'returns error if obj doesnt exist' do -# response = epp_plain_request(delete_contact_xml, :xml) -# response[:msg].should == 'Object does not exist' -# response[:result_code].should == '2303' -# end - -# it 'fails if contact has associated domain' do -# Fabricate( -# :domain, -# registrar: @registrar1, -# owner_contact: Fabricate( -# :contact, -# code: 'dwa1234', -# # created_by_id: registrar1.id, -# registrar: @registrar1) -# ) -# Domain.last.owner_contact.address.present?.should == true -# response = epp_plain_request(delete_contact_xml({ id: { value: 'dwa1234' } }), :xml) - -# response[:msg].should == 'Object association prohibits operation' -# response[:result_code].should == '2305' - -# Domain.last.owner_contact.present?.should == true -# end -# end - -# context 'check command' do -# it 'fails if request is invalid' do -# xml = @epp_xml.check({ uid: { value: '123asde' } }) -# response = epp_plain_request(xml, :xml) - -# response[:results][0][:msg].should == 'Required parameter missing: id' -# response[:results][0][:result_code].should == '2003' -# response[:results].count.should == 1 -# end - -# it 'returns info about contact availability' do -# Fabricate(:contact, code: 'check-1234') - -# response = epp_plain_request(check_multiple_contacts_xml, :xml) - -# response[:msg].should == 'Command completed successfully' -# response[:result_code].should == '1000' -# ids = response[:parsed].css('resData chkData id') - -# ids[0].attributes['avail'].text.should == '0' -# ids[1].attributes['avail'].text.should == '1' - -# ids[0].text.should == 'check-1234' -# ids[1].text.should == 'check-4321' -# end -# end - -# # context 'info command' do -# # before :all do -# # @registrar1_contact = Fabricate(:contact, code: 'info-4444', registrar: @registrar1, -# # name: 'Johnny Awesome', address: Fabricate(:address)) -# # end - -# # fit 'return info about contact' do -# # login_as :registrar2 do -# # xml = @epp_xml.info(id: { value: @registrar1_contact.code }) -# # response = epp_plain_request(xml, :xml) -# # response[:msg].should == 'Command completed successfully' -# # response[:result_code].should == '1000' - -# # contact = response[:parsed].css('resData chkData') -# # contact.css('name').first.text.should == 'Johnny Awesome' -# # end -# # end - -# # it 'fails if request invalid' do -# # response = epp_plain_request(@epp_xml.info({ wrongid: { value: '123123' } }), :xml) -# # response[:results][0][:msg].should == 'Required parameter missing: id' -# # response[:results][0][:result_code].should == '2003' -# # response[:results].count.should == 1 -# # end - -# # it 'returns error when object does not exist' do -# # response = epp_plain_request(info_contact_xml({ id: { value: 'no-contact' } }), :xml) -# # response[:msg].should == 'Object does not exist' -# # response[:result_code].should == '2303' -# # response[:results][0][:value].should == 'no-contact' -# # end - -# # # it 'returns auth error for non-owner with wrong password' do -# # # @contact = Fabricate(:contact, -# # # registrar: registrar2, code: 'info-4444', name: 'Johnny Awesome', auth_info: 'asde', -# # # address: Fabricate(:address), disclosure: Fabricate(:contact_disclosure, name: false)) - -# # # xml = @epp_xml.info({ id: { value: @contact.code }, authInfo: { pw: { value: 'asdesde' } } }) -# # # response = epp_plain_request(xml, :xml, :registrar1) - -# # # expect(response[:result_code]).to eq('2200') -# # # expect(response[:msg]).to eq('Authentication error') -# # # end - -# # context 'about disclose' do -# # it 'discloses items with wrong password when queried by owner' do -# # @contact = Fabricate(:contact, -# # registrar: registrar1, code: 'info-4444', -# # name: 'Johnny Awesome', auth_info: 'asde', -# # address: Fabricate(:address), disclosure: Fabricate(:contact_disclosure, name: false)) - -# # xml = @epp_xml.info({ id: { value: @contact.code } }) -# # login_as :registrar1 do -# # response = epp_plain_request(xml, :xml) -# # contact = response[:parsed].css('resData chkData') - -# # expect(response[:result_code]).to eq('1000') -# # expect(response[:msg]).to eq('Command completed successfully') -# # expect(contact.css('name').first.text).to eq('Johnny Awesome') -# # end -# # end - -# # it 'doesn\'t disclose items to non-owner with right password' do -# # @contact = Fabricate(:contact, registrar: registrar2, code: 'info-4444', -# # name: 'Johnny Awesome', auth_info: 'password', -# # address: Fabricate(:address), disclosure: Fabricate(:contact_disclosure, name: false)) - -# # xml = @epp_xml.info({ id: { value: @contact.code }, authInfo: { pw: { value: 'password' } } }) -# # response = epp_plain_request(xml, :xml, :registrar1) -# # contact = response[:parsed].css('resData chkData') - -# # expect(response[:result_code]).to eq('1000') -# # expect(response[:msg]).to eq('Command completed successfully') -# # expect(contact.css('chkData postalInfo name').first).to eq(nil) -# # end - -# # it 'discloses items to owner' do -# # @contact = Fabricate(:contact, registrar: registrar1, code: 'info-4444', name: 'Johnny Awesome', -# # auth_info: 'password', -# # address: Fabricate(:address), disclosure: Fabricate(:contact_disclosure, name: false)) - -# # xml = @epp_xml.info({ id: { value: @contact.code } }) -# # response = epp_plain_request(xml, :xml, :registrar1) -# # contact = response[:parsed].css('resData chkData') - -# # expect(response[:result_code]).to eq('1000') -# # expect(response[:msg]).to eq('Command completed successfully') -# # expect(contact.css('name').first.text).to eq('Johnny Awesome') -# # end - -# # it 'doesn\'t disclose private elements' do -# # Fabricate(:contact, code: 'info-4444', auth_info: '2fooBAR', registrar: registrar2, -# # disclosure: Fabricate(:contact_disclosure, name: true, email: false, phone: false)) - -# # xml = @epp_xml.info({ id: { value: 'info-4444' }, authInfo: { pw: { value: '2fooBAR' } } }) - -# # response = epp_plain_request(xml, :xml, :registrar1) -# # contact = response[:parsed].css('resData chkData') - -# # expect(response[:result_code]).to eq('1000') - -# # expect(contact.css('chkData phone')).to eq(contact.css('chkData disclose phone')) -# # expect(contact.css('chkData phone').count).to eq(1) -# # expect(contact.css('chkData email')).to eq(contact.css('chkData disclose email')) -# # expect(contact.css('chkData email').count).to eq(1) -# # expect(contact.css('postalInfo name').present?).to be(true) -# # end -# # end - -# # it 'does not display unassociated object without password' do -# # xml = @epp_xml.info(id: { value: @registrar1_contact.code }) -# # response = epp_plain_request(xml, :xml, :registrar2) -# # expect(response[:result_code]).to eq('2003') -# # expect(response[:msg]).to eq('Required parameter missing: pw') -# # end - -# # it 'does not display unassociated object with wrong password' do -# # login_as :registrar2 -# # xml = @epp_xml.info(id: { value: @registrar1_contact.code }, -# # authInfo: { pw: { value: 'wrong-pw' } }) -# # response = epp_plain_request(xml, :xml) - -# # response[:msg].should == 'Authentication error' -# # response[:result_code].should == '2200' -# # end -# # end - -# context 'renew command' do -# it 'returns 2101-unimplemented command' do -# response = epp_plain_request('contacts/renew.xml') - -# response[:msg].should == 'Unimplemented command' -# response[:result_code].should == '2101' -# end -# end -# end -# end +require 'rails_helper' + +describe 'EPP Contact', epp: true do + before :all do + create_settings + create_disclosure_settings + @registrar1 = Fabricate(:registrar1) + @registrar2 = Fabricate(:registrar2) + @epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') + + Fabricate(:api_user, username: 'registrar1', registrar: @registrar1) + Fabricate(:api_user, username: 'registrar2', registrar: @registrar2) + + login_as :registrar1 + + Contact.skip_callback(:create, :before, :generate_code) + Contact.skip_callback(:create, :before, :generate_auth_info) + + @contact = Fabricate(:contact, registrar: @registrar1) + @legal_document = { + legalDocument: { + value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', + attrs: { type: 'pdf' } + } + } + end + + after :all do + Contact.set_callback(:create, :before, :generate_code) + Contact.set_callback(:create, :before, :generate_auth_info) + end + + context 'with valid user' do + context 'create command' do + def create_request(overwrites = {}) + defaults = { + postalInfo: { + name: { value: 'John Doe' }, + addr: { + street: { value: '123 Example' }, + city: { value: 'Tallinn' }, + cc: { value: 'EE' } + } + }, + voice: { value: '+372.1234567' }, + email: { value: 'test@example.example' }, + ident: { value: '37605030299', attrs: { type: 'priv', cc: 'EE' } } + } + create_xml = @epp_xml.create(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(create_xml, :xml) + end + + it 'fails if request xml is missing' do + response = epp_plain_request(@epp_xml.create, :xml) + response[:results][0][:msg].should == + 'Required parameter missing: create > create > postalInfo > name' + response[:results][1][:msg].should == + 'Required parameter missing: create > create > postalInfo > addr > city' + response[:results][2][:msg].should == + 'Required parameter missing: create > create > postalInfo > addr > cc' + response[:results][3][:msg].should == + 'Required parameter missing: create > create > ident' + response[:results][4][:msg].should == + 'Required parameter missing: create > create > voice' + response[:results][5][:msg].should == + 'Required parameter missing: create > create > email' + response[:results][6][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument' + + response[:results][0][:result_code].should == '2003' + response[:results][1][:result_code].should == '2003' + response[:results][2][:result_code].should == '2003' + response[:results][3][:result_code].should == '2003' + response[:results][4][:result_code].should == '2003' + response[:results][5][:result_code].should == '2003' + response[:results][6][:result_code].should == '2003' + + response[:results].count.should == 7 + end + + it 'successfully creates a contact' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + @contact = Contact.last + + @contact.registrar.should == @registrar1 + @registrar1.api_users.should include(@contact.creator) + @contact.ident.should == '37605030299' + @contact.address.street.should == '123 Example' + @contact.legal_documents.count.should == 1 + + log = ApiLog::EppLog.last + log.request_command.should == 'create' + log.request_object.should == 'contact' + log.request_successful.should == true + log.api_user_name.should == '1-api-registrar1' + log.api_user_registrar.should == 'registrar1' + end + + it 'successfully saves ident type' do + response = create_request( + { ident: { value: '1990-22-12', attrs: { type: 'birthday' } } } + ) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + Contact.last.ident_type.should == 'birthday' + end + + it 'successfully adds registrar' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + Contact.last.registrar.should == @registrar1 + end + + it 'returns result data upon success' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + id = response[:parsed].css('resData creData id').first + cr_date = response[:parsed].css('resData creData crDate').first + + id.text.length.should == 8 + # 5 seconds for what-ever weird lag reasons might happen + cr_date.text.to_time.should be_within(5).of(Time.now) + end + end + + context 'update command' do + before :all do + @contact = + Fabricate( + :contact, + # created_by_id: 1, + registrar: @registrar1, + email: 'not_updated@test.test', + code: 'sh8013', + auth_info: 'password' + ) + end + + def update_request(overwrites = {}) + defaults = { + id: { value: 'asd123123er' }, + authInfo: { pw: { value: 'password' } }, + chg: { + postalInfo: { + name: { value: 'John Doe Edited' } + }, + voice: { value: '+372.7654321' }, + email: { value: 'edited@example.example' }, + disclose: { + value: { + voice: { value: '' }, + email: { value: '' } + }, attrs: { flag: '0' } + } + } + } + update_xml = @epp_xml.update(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(update_xml, :xml) + end + + it 'fails if request is invalid' do + response = epp_plain_request(@epp_xml.update, :xml) + + response[:results][0][:msg].should == + 'Required parameter missing: add, rem or chg' + response[:results][0][:result_code].should == '2003' + response[:results][1][:msg].should == + 'Required parameter missing: update > update > id' + response[:results][1][:result_code].should == '2003' + response[:results][2][:msg].should == + 'Required parameter missing: update > update > authInfo > pw' + response[:results][2][:result_code].should == '2003' + response[:results][3][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument' + response[:results][3][:result_code].should == '2003' + response[:results].count.should == 4 + end + + it 'returns error if obj doesnt exist' do + response = update_request({ id: { value: 'not-exists' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results].count.should == 1 + end + + it 'is succesful' do + response = update_request({ id: { value: 'sh8013' } }) + + response[:msg].should == 'Command completed successfully' + @contact.reload + @contact.name.should == 'John Doe Edited' + @contact.email.should == 'edited@example.example' + end + + it 'fails with wrong authentication info' do + login_as :registrar2 do + response = update_request({ id: { value: 'sh8013' } }) + response[:msg].should == 'Authorization error' + response[:result_code].should == '2201' + end + end + + it 'returns phone and email error' do + response = update_request({ + id: { value: 'sh8013' }, + chg: { + voice: { value: '123213' }, + email: { value: 'wrong' } + } + }) + + response[:results][0][:msg].should == 'Phone nr is invalid' + response[:results][0][:result_code].should == '2005' + response[:results][1][:msg].should == 'Email is invalid' + response[:results][1][:result_code].should == '2005' + end + end + + context 'delete command' do + before do + @contact = Fabricate(:contact, registrar: @registrar1) + end + + def delete_request(overwrites = {}) + defaults = { + id: { value: @contact.code }, + authInfo: { pw: { value: @contact.auth_info } } + } + delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(delete_xml, :xml) + end + + it 'fails if request is invalid' do + response = epp_plain_request(@epp_xml.delete, :xml) + + response[:results][0][:msg].should == + 'Required parameter missing: delete > delete > id' + response[:results][0][:result_code].should == '2003' + response[:results][1][:msg].should == + 'Required parameter missing: delete > delete > authInfo > pw' + response[:results][1][:result_code].should == '2003' + response[:results][2][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument' + response[:results][2][:result_code].should == '2003' + response[:results].count.should == 3 + end + + it 'returns error if obj doesnt exist' do + response = delete_request({ id: { value: 'not-exists' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results].count.should == 1 + end + + it 'deletes contact' do + response = delete_request + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:clTRID].should == 'ABC-12345' + + Contact.find_by_id(@contact.id).should == nil + end + + it 'fails if contact has associated domain' do + @domain = Fabricate(:domain, registrar: @registrar1, owner_contact: @contact) + @domain.owner_contact.address.present?.should == true + + response = delete_request + response[:msg].should == 'Object association prohibits operation' + response[:result_code].should == '2305' + response[:results].count.should == 1 + + @domain.owner_contact.present?.should == true + end + + it 'fails with wrong authentication info' do + login_as :registrar2 do + response = delete_request + response[:msg].should == 'Authorization error' + response[:result_code].should == '2201' + response[:results].count.should == 1 + end + end + end + + context 'check command' do + def check_request(overwrites = {}) + defaults = { + id: { value: @contact.code }, + authInfo: { pw: { value: @contact.auth_info } } + } + xml = @epp_xml.check(defaults.deep_merge(overwrites)) + epp_plain_request(xml, :xml) + end + + it 'fails if request is invalid' do + response = epp_plain_request(@epp_xml.check, :xml) + + response[:results][0][:msg].should == 'Required parameter missing: check > check > id' + response[:results][0][:result_code].should == '2003' + response[:results].count.should == 1 + end + + it 'returns info about contact availability' do + Fabricate(:contact, code: 'check-1234') + + response = epp_plain_request(check_multiple_contacts_xml, :xml) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + ids = response[:parsed].css('resData chkData id') + + ids[0].attributes['avail'].text.should == '0' + ids[1].attributes['avail'].text.should == '1' + + ids[0].text.should == 'check-1234' + ids[1].text.should == 'check-4321' + end + end + + context 'info command' do + def info_request(overwrites = {}) + defaults = { + id: { value: @contact.code }, + authInfo: { pw: { value: @contact.auth_info } } + } + xml = @epp_xml.info(defaults.deep_merge(overwrites)) + epp_plain_request(xml, :xml) + end + + it 'fails if request invalid' do + response = epp_plain_request(@epp_xml.info, :xml) + response[:results][0][:msg].should == + 'Required parameter missing: info > info > id' + response[:results][0][:result_code].should == '2003' + response[:results].count.should == 1 + end + + it 'returns error when object does not exist' do + response = info_request({ id: { value: 'no-contact' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results][0][:value].should == 'no-contact' + response[:results].count.should == 1 + end + + it 'return info about contact' do + @registrar1_contact = Fabricate( + :contact, code: 'info-4444', registrar: @registrar1, + name: 'Johnny Awesome', address: Fabricate(:address)) + + response = info_request({ id: { value: @registrar1_contact.code } }) + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + contact = response[:parsed].css('resData chkData') + contact.css('name').first.text.should == 'Johnny Awesome' + end + + it 'returns no authorization error for wrong password when owner' do + response = info_request({ authInfo: { pw: { value: 'wrong-pw' } } }) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:results].count.should == 1 + end + + it 'returns no authorization error for wrong user but correct pw' do + login_as :registrar2 do + response = info_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:results].count.should == 1 + end + end + + it 'returns authorization error for wrong user and wrong pw' do + login_as :registrar2 do + response = info_request({ authInfo: { pw: { value: 'wrong-pw' } } }) + response[:msg].should == 'Authorization error' + response[:result_code].should == '2201' + response[:results].count.should == 1 + end + end + end + + context 'renew command' do + it 'returns 2101-unimplemented command' do + response = epp_plain_request('contacts/renew.xml') + + response[:msg].should == 'Unimplemented command' + response[:result_code].should == '2101' + end + end + end + + def check_multiple_contacts_xml + ' + + + + + check-1234 + check-4321 + + + ABC-12345 + + ' + end +end diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 5ba4c4159..cf49a0987 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -16,7 +16,7 @@ describe 'EPP Domain', epp: true do Fabricate(:contact, code: 'citizen_1234') Fabricate(:contact, code: 'sh8013') Fabricate(:contact, code: 'sh801333') - Fabricate(:contact, code: 'juridical_1234', ident_type: 'ico') + Fabricate(:contact, code: 'juridical_1234', ident_type: 'bic') Fabricate(:reserved_domain) @uniq_no = proc { @i ||= 0; @i += 1 } @@ -206,8 +206,16 @@ describe 'EPP Domain', epp: true do it 'does not create domain without nameservers' do xml = domain_create_xml(ns: []) response = epp_plain_request(xml, :xml) - response[:result_code].should == '2003' - response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr' + + response[:results][0][:msg].should == + 'Required parameter missing: create > create > ns' + response[:results][0][:result_code].should == '2003' + + response[:results][1][:msg].should == + 'Required parameter missing: create > create > ns > hostAttr' + response[:results][1][:result_code].should == '2003' + + response[:results].count.should == 2 end it 'does not create domain with too many nameservers' do @@ -294,8 +302,8 @@ describe 'EPP Domain', epp: true do xml = domain_create_xml(period_value: 365, period_unit: 'd') response = epp_plain_request(xml, :xml) - response[:result_code].should == '1000' response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' Domain.first.valid_to.should == Date.today + 1.year end @@ -661,8 +669,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:result_code].should == '2004' response[:msg].should == 'Admin contacts count must be between 1-10' + response[:result_code].should == '2004' response[:clTRID].should == 'ABC-12345' Domain.count.should == domain_count @@ -678,8 +686,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:result_code].should == '2306' response[:msg].should == 'Admin contact can be only citizen' + response[:result_code].should == '2306' end end diff --git a/spec/fabricators/contact_fabricator.rb b/spec/fabricators/contact_fabricator.rb index c483ad554..0462f7a61 100644 --- a/spec/fabricators/contact_fabricator.rb +++ b/spec/fabricators/contact_fabricator.rb @@ -1,10 +1,11 @@ Fabricator(:contact) do + code { "sh#{Faker::Number.number(8)}" } name { sequence(:name) { |i| "#{Faker::Name.name}#{i}" } } phone '+372.12345678' email Faker::Internet.email ident '37605030299' - code { "sh#{Faker::Number.number(8)}" } - ident_type 'op' + ident_type 'priv' + ident_country_code 'EE' auth_info 'ccds4324pok' address registrar { Fabricate(:registrar, name: Faker::Company.name, reg_no: Faker::Company.duns_number) } diff --git a/spec/fabricators/domain_fabricator.rb b/spec/fabricators/domain_fabricator.rb index d6197b6e4..fd5c209e6 100644 --- a/spec/fabricators/domain_fabricator.rb +++ b/spec/fabricators/domain_fabricator.rb @@ -1,5 +1,5 @@ Fabricator(:domain) do - name { "#{Faker::Internet.domain_word}.ee" } + name { "fabricate_name#{rand(1_000_000)}.ee" } valid_to Date.new(2014, 8, 7) period 1 period_unit 'y' diff --git a/spec/models/contact_disclosure_spec.rb b/spec/models/contact_disclosure_spec.rb deleted file mode 100644 index f8c268fca..000000000 --- a/spec/models/contact_disclosure_spec.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'rails_helper' - -describe ContactDisclosure do - it { should belong_to(:contact) } - - context 'about class' do - it 'should have versioning enabled?' do - ContactDisclosure.paper_trail_enabled_for_model?.should == true - end - - it 'should have custom log prexied table name for versions table' do - ContactDisclosureVersion.table_name.should == 'log_contact_disclosures' - end - end - - context 'with invalid attribute' do - before :all do - @contact_disclosure = ContactDisclosure.new - end - - it 'should not be valid' do - @contact_disclosure.valid? - @contact_disclosure.errors.full_messages.should match_array([ - ]) - end - - it 'should not have any versions' do - @contact_disclosure.versions.should == [] - end - end - - context 'with valid attributes' do - before :all do - @contact_disclosure = Fabricate(:contact_disclosure) - end - - it 'should be valid' do - @contact_disclosure.valid? - @contact_disclosure.errors.full_messages.should match_array([]) - end - - it 'should be valid twice' do - @contact_disclosure = Fabricate(:contact_disclosure) - @contact_disclosure.valid? - @contact_disclosure.errors.full_messages.should match_array([]) - end - - it 'should have one version' do - with_versioning do - @contact_disclosure.versions.should == [] - @contact_disclosure.name = false - @contact_disclosure.save - @contact_disclosure.errors.full_messages.should match_array([]) - @contact_disclosure.versions.size.should == 1 - end - end - end - -end - -describe '.extract_attributes' do - it 'should return empty hash for empty arguments' do - result = ContactDisclosure.extract_attributes(Nokogiri::XML::Document.new) - expect(result).to eq({}) - end - - it 'should return empty hash if no disclosure' do - parsed_frame = Nokogiri::XML(create_contact_xml).remove_namespaces! - result = ContactDisclosure.extract_attributes(parsed_frame) - expect(result).to eq({}) - end - - # TODO: remodel create contact xml to support disclosure - it 'should return disclosure has if disclosure' do - epp_xml = EppXml::Contact.new - xml = epp_xml.create( - { - disclose: { value: { - voice: { value: '' }, - addr: { value: '' }, - name: { value: '' }, - org_name: { value: '' }, - email: { value: '' }, - fax: { value: '' } - }, attrs: { flag: '0' } - } }) - parsed_frame = Nokogiri::XML(xml).remove_namespaces! - result = ContactDisclosure.extract_attributes(parsed_frame) - expect(result).to eq({ phone: '0', email: '0', fax: '0', address: '0', name: '0', org_name: '0' }) - end -end diff --git a/spec/models/contact_spec.rb b/spec/models/contact_spec.rb index 58b54cacf..d605e9781 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -39,11 +39,11 @@ describe Contact do end it 'should not have creator' do - @contact.cr_id.should == nil + @contact.creator.should == nil end it 'should not have updater' do - @contact.up_id.should == nil + @contact.updator.should == nil end it 'phone should return false' do @@ -52,6 +52,42 @@ describe Contact do @contact.errors[:phone].should == ["Phone nr is invalid"] end + it 'should require country code when bic' do + @contact.ident_type = 'bic' + @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_type = 'bic' + @contact.ident_country_code = 'EE' + @contact.valid? + + @contact.errors[:ident_country_code].should == [] + end + + it 'should require valid country code' do + @contact.ident_type = 'bic' + @contact.ident_country_code = 'INVALID' + @contact.valid? + + @contact.errors[:ident_country_code].should == ['is not following ISO_3166-1 alpha 2 format'] + end + + it 'should convert to alpha2 country code' do + @contact.ident_type = 'bic' + @contact.ident_country_code = 'ee' + @contact.valid? + + @contact.ident_country_code.should == 'EE' + end + it 'should not have any versions' do @contact.versions.should == [] end @@ -87,18 +123,12 @@ describe Contact do @contact.relations_with_domain?.should == false end - # it 'ico should be valid' do - # @contact.ident_type = 'ico' - # @contact.ident = '1234' - # @contact.errors.full_messages.should match_array([]) - # end - - # it 'ident should return false' do - # puts @contact.ident_type - # @contact.ident = '123abc' - # @contact.valid? - # @contact.errors.full_messages.should_not == [] - # end + it 'bic should be valid' do + @contact.ident_type = 'bic' + @contact.ident = '1234' + @contact.valid? + @contact.errors.full_messages.should match_array([]) + end context 'as birthday' do before :all do @@ -119,7 +149,8 @@ describe Contact do invalid.each do |date| @contact.ident = date @contact.valid? - @contact.errors.full_messages.should == ["Ident is invalid"] + @contact.errors.full_messages.should == + ["Ident Ident not in valid birthady format, should be YYYY-MM-DD"] end end end @@ -177,59 +208,10 @@ describe Contact do @contact.auth_info.should == 'qwe321' end end - - context 'with creator' do - before :all do - # @contact.created_by = @api_user - end - - # TODO: change cr_id to something else - it 'should return username of creator' do - # @contact.cr_id.should == 'gitlab' - end - end - - context 'with updater' do - before :all do - # @contact.updated_by = @api_user - end - - # TODO: change up_id to something else - it 'should return username of updater' do - # @contact.up_id.should == 'gitlab' - end - - end end end end -# TODO: investigate it a bit more -# describe Contact, '#relations_with_domain?' do -# context 'with relation' do -# before :all do -# create_settings -# Fabricate(:domain) -# @contact = Fabricate(:contact) -# end - -# it 'should have relation with domain' do -# @contact.relations_with_domain?.should == true -# end -# end -# end - -describe Contact, '.extract_params' do - it 'returns params hash'do - ph = { id: '123123', email: 'jdoe@example.com', authInfo: { pw: 'asde' }, - postalInfo: { name: 'fred', addr: { cc: 'EE' } } } - Contact.extract_attributes(ph).should == { - name: 'fred', - email: 'jdoe@example.com' - } - end -end - describe Contact, '.check_availability' do before do Fabricate(:contact, code: 'asd12') diff --git a/spec/support/epp.rb b/spec/support/epp.rb index 3bceb034d..35b24a942 100644 --- a/spec/support/epp.rb +++ b/spec/support/epp.rb @@ -112,7 +112,7 @@ module Epp end def next_domain_name - "example#{@uniq_no.call}.ee" + "example#{rand(100000000)}.ee" end ### REQUEST TEMPLATES ### diff --git a/spec/support/epp_contact_xml_helper.rb b/spec/support/epp_contact_xml_helper.rb deleted file mode 100644 index 693e12ae7..000000000 --- a/spec/support/epp_contact_xml_helper.rb +++ /dev/null @@ -1,87 +0,0 @@ -module EppContactXmlHelper - def create_contact_xml(xml_params = {}) - defaults = { - postalInfo: { - name: { value: 'John Doe' }, - addr: { - street: { value: '123 Example' }, - city: { value: 'Tallinn' }, - cc: { value: 'EE' } - } - }, - voice: { value: '+372.1234567' }, - email: { value: 'test@example.example' }, - ident: { value: '37605030299', attrs: { type: 'op' } } - } - - xml_params = defaults.deep_merge(xml_params) - epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - epp_xml.create(xml_params) - end - - def update_contact_xml(xml_params = {}) - defaults = { - id: { value: 'asd123123er' }, - authInfo: { pw: { value: 'password' } }, - chg: { - postalInfo: { - name: { value: 'John Doe Edited' } - }, - voice: { value: '+372.7654321' }, - email: { value: 'edited@example.example' }, - disclose: { - value: { - voice: { value: '' }, - email: { value: '' } - }, attrs: { flag: '0' } - } - } - } - xml_params = defaults.deep_merge(xml_params) - epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - epp_xml.update(xml_params) - end - - def delete_contact_xml(xml_params = {}) - defaults = { id: { value: 'sh8012' } } - xml_params = defaults.deep_merge(xml_params) - epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - epp_xml.delete(xml_params) - end - - def info_contact_xml(xml_params = {}) - defaults = { id: { value: 'sh8012' }, authInfo: { pw: { value: 'password' } } } - xml_params = defaults.deep_merge(xml_params) - epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - epp_xml.info(xml_params) - end - - def check_contact_xml(xml_params = {}) - defaults = { - id: { value: 'ad123c3' } - } - xml_params = defaults.deep_merge(xml_params) - epp_xml = EppXml::Contact.new(cl_trid: 'ABC-12345') - epp_xml.check(xml_params) - end - - def check_multiple_contacts_xml - ' - - - - - check-1234 - check-4321 - - - ABC-12345 - - ' - end -end - -RSpec.configure do |c| - c.include EppContactXmlHelper -end