From 6e2eaf6fff2bd63fc82ff29e2ad2b3b976da5c88 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:48:02 +0200 Subject: [PATCH 01/31] Fixed seeds for registrar updates --- db/schema.rb | 3 ++- db/seeds.rb | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 26b58e3f9..31bc2bcd6 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: 20150203135303) do +ActiveRecord::Schema.define(version: 20150212125339) 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: 20150203135303) 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| diff --git a/db/seeds.rb b/db/seeds.rb index 85757ad79..8e715d3e9 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! From bb9793dc4a06d2683c418503cf0cf3149c75ea54 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:48:02 +0200 Subject: [PATCH 02/31] Fixed seeds for registrar updates --- db/schema.rb | 3 ++- db/seeds.rb | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 614fe9ae6..c963f2c75 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: 20150212125339) 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| 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! From e04403403cf9a57c7f4347b72453e1fc99035c2d Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:53:38 +0200 Subject: [PATCH 03/31] Epp doc syntax fixes --- doc/epp/contact.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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) From e85d846c08779e8f3a262a244fedab1ca6c1986a Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:55:04 +0200 Subject: [PATCH 04/31] Allow blank for requires helper --- app/controllers/epp_controller.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/epp_controller.rb b/app/controllers/epp_controller.rb index 28eff040f..4749c1ff0 100644 --- a/app/controllers/epp_controller.rb +++ b/app/controllers/epp_controller.rb @@ -84,12 +84,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) From fe60a62772882880c65c3c4c018cfe5c750965e7 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:55:21 +0200 Subject: [PATCH 05/31] Added new requires format to contacts controller --- app/controllers/epp/contacts_controller.rb | 61 ++++++++++------------ app/helpers/whodunnit_helper.rb | 25 --------- 2 files changed, 28 insertions(+), 58 deletions(-) delete mode 100644 app/helpers/whodunnit_helper.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 6aa5efce3..fefdce438 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,5 +1,14 @@ class Epp::ContactsController < EppController - helper WhodunnitHelper ## Refactor this? + 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 + render_epp_response 'epp/contacts/info' + end def create @contact = Contact.new(contact_and_address_attributes) @@ -39,17 +48,6 @@ class Epp::ContactsController < EppController render_epp_response '/epp/contacts/check' end - 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 - render_epp_response 'epp/contacts/info' - end - def renew epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') } handle_errors @@ -61,20 +59,22 @@ class Epp::ContactsController < EppController ## 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? + @prefix = 'create > create >' + requires 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' 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']]) + def validate_update + @prefix = 'update > update >' + requires 'id' + + 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 end def contact_exists?(code) @@ -83,13 +83,6 @@ class Epp::ContactsController < EppController 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'] @@ -115,11 +108,13 @@ class Epp::ContactsController < EppController ## SHARED def find_contact - contact = Contact.find_by(code: @ph[:id]) - unless contact + contact_code = params[:parsed_frame].css('id').text.strip.downcase + contact = Contact.find_by(code: contact_code) + + if contact.blank? epp_errors << { code: '2303', msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: @ph[:id] } } + value: { obj: 'id', val: contact_code } } end contact 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 From afb052848eb3ba21a37db2793894133a8b8a282e Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 12 Feb 2015 12:56:13 +0200 Subject: [PATCH 06/31] Rubocop syntax fix --- app/controllers/epp/domains_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 26eeb7eb6..319c1c041 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -206,7 +206,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_api_user.registrar) && secure[:secure] == true epp_errors << { code: '2302', msg: I18n.t('errors.messages.domain_exists_but_belongs_to_other_registrar'), From 67a7c6691793bc579ebed5067f64288b9e4353f9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:38:36 +0200 Subject: [PATCH 07/31] Refactored contact with ability --- Guardfile | 10 +- app/controllers/admin_controller.rb | 1 - app/controllers/application_controller.rb | 24 +- app/controllers/epp/contacts_controller.rb | 235 ++--- app/controllers/epp/domains_controller.rb | 2 + app/controllers/epp/errors_controller.rb | 2 + app/controllers/epp/keyrelays_controller.rb | 2 + app/controllers/epp/polls_controller.rb | 4 +- app/controllers/epp/sessions_controller.rb | 2 + app/controllers/epp_controller.rb | 17 +- app/controllers/sessions_controller.rb | 2 + app/models/ability.rb | 15 +- app/models/api_user.rb | 5 + app/models/concerns/version_session.rb | 2 +- app/models/concerns/versions.rb | 8 +- app/models/contact.rb | 139 +-- app/models/epp/contact.rb | 87 ++ app/views/admin/contacts/index.haml | 10 +- .../admin/contacts/partials/_address.haml | 26 +- .../admin/contacts/partials/_general.haml | 30 +- app/views/admin/contacts/show.haml | 2 +- .../epp/contacts/_postal_info.xml.builder | 18 +- app/views/epp/contacts/check.xml.builder | 7 +- app/views/epp/contacts/info.xml.builder | 19 +- config/locales/en.yml | 29 +- .../20150212125339_add_state_to_address.rb | 5 + spec/epp/contact_spec.rb | 862 +++++++++--------- spec/epp/domain_spec.rb | 14 +- spec/fabricators/domain_fabricator.rb | 2 +- spec/models/contact_disclosure_spec.rb | 91 -- spec/models/contact_spec.rb | 47 +- spec/support/epp.rb | 2 +- spec/support/epp_contact_xml_helper.rb | 87 -- 33 files changed, 763 insertions(+), 1045 deletions(-) create mode 100644 app/models/epp/contact.rb create mode 100644 db/migrate/20150212125339_add_state_to_address.rb delete mode 100644 spec/models/contact_disclosure_spec.rb delete mode 100644 spec/support/epp_contact_xml_helper.rb diff --git a/Guardfile b/Guardfile index eab74ee21..955ca3b4e 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$}) 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..698cbfa88 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,6 @@ class ApplicationController < ActionController::Base + check_authorization + # 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,22 @@ 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 current_ability + if defined?(current_api_user) && current_api_user.present? + current_api_user.ability + else + current_user.ability + end + 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 +50,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 fefdce438..c25050aad 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,183 +1,114 @@ class Epp::ContactsController < EppController + 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 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]) + 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 - @prefix = 'create > create >' - requires 'postalInfo > name', 'postalInfo > addr > city', - 'postalInfo > addr > cc', 'ident', 'voice', 'email' + def find_password + @password = params[:parsed_frame].css('authInfo pw').text + end + + def find_contact + 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 + end + + # + # Validations + # + def validate_info + @prefix = 'info > info >' + requires 'id' + end + + def validate_check + @prefix = 'check > check >' + requires 'id' + end + + def validate_create + @prefix = 'create > create >' + requires( + 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' + ) end - ## UPDATE def validate_update @prefix = 'update > update >' - requires 'id' - 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' 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 - - ## 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_code = params[:parsed_frame].css('id').text.strip.downcase - contact = Contact.find_by(code: contact_code) - - if contact.blank? - epp_errors << { code: '2303', - msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: contact_code } } - end - 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 - 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 - end - - def update_rights? - pw = @ph.try(:[], :authInfo).try(:[], :pw) - return true if pw && @contact.auth_info_matches(pw) - epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') } - false - end - - def contact_and_address_attributes(type = :create) - case type - when :update - # TODO: support for rem/add - contact_hash = merge_attribute_hash(@ph[:chg], type).delete_if { |_k, v| v.empty? } - else - contact_hash = merge_attribute_hash(@ph, type) - end - contact_hash[:ident_type] = ident_type unless ident_type.nil? - contact_hash - end - - def merge_attribute_hash(prms, type) - contact_hash = Contact.extract_attributes(prms, type) - contact_hash = contact_hash.merge( - Address.extract_attributes((prms.try(:[], :postalInfo) || [])) - ) - contact_hash[:disclosure_attributes] = - ContactDisclosure.extract_attributes(params[:parsed_frame]) - - contact_hash - 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 - end - - def validate_params - return true if @ph - epp_errors << { code: '2001', msg: t(:'errors.messages.epp_command_syntax_error') } - false + @prefix = 'delete > delete >' + requires 'id', 'authInfo > pw' end end diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 319c1c041..eac99120e 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) 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 1485001a8..21161c7a0 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 4749c1ff0..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))}" @@ -112,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..7e1247c1a 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,6 @@ class SessionsController < Devise::SessionsController + skip_authorization_check only: [:login, :create] + def create # TODO: Create ID Card login here: # this is just testing config diff --git a/app/models/ability.rb b/app/models/ability.rb index 47fc6c209..9980e9709 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,16 +2,25 @@ 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? - 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..1dd0b6ea6 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -13,6 +13,11 @@ class ApiUser < User 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..539c0d1a2 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 @@ -19,25 +18,20 @@ class Contact < ActiveRecord::Base 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 :code, uniqueness: { message: :epp_id_taken } - 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_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' @@ -54,6 +48,32 @@ class Contact < ActiveRecord::Base CONTACT_TYPE_ADMIN = 'admin' CONTACT_TYPES = [CONTACT_TYPE_TECH, CONTACT_TYPE_ADMIN] + 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_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 @@ -66,15 +86,6 @@ class Contact < ActiveRecord::Base 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 end @@ -83,18 +94,6 @@ class Contact < ActiveRecord::Base 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 - end - # generate random id for contact def generate_code self.code = SecureRandom.hex(4) @@ -114,6 +113,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? @@ -122,76 +123,4 @@ class Contact < ActiveRecord::Base end 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})" } } - end - end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb new file mode 100644 index 000000000..bb0af23cd --- /dev/null +++ b/app/models/epp/contact.rb @@ -0,0 +1,87 @@ +# rubocop: disable Metrics/ClassLength +class Epp::Contact < Contact + include EppErrors + + # disable STI, there is type column present + self.inheritance_column = :sti_disabled + + class << self + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity + 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').text + end + + at[:address_attributes] = {} + 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? + at + end + # rubocop: enable Metrics/PerceivedComplexity + # rubocop: enable Metrics/CyclomaticComplexity + + def new(frame) + return super if frame.blank? + super(attrs_from(frame)) + end + + def parse_legal_document_from_frame(parsed_frame) + ld = parsed_frame.css('legalDocument').first + return nil unless ld + + { + body: ld.text, + type: ld['type'] + } + 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] + ] + } + end + + def update_attributes(frame) + return super if frame.blank? + at = {}.with_indifferent_access + at.deep_merge!(self.class.attrs_from(frame.css('chg'))) + super(at) + end + + def attach_legal_document(legal_document_data) + return unless legal_document_data + + legal_documents.build( + document_type: legal_document_data[:type], + body: legal_document_data[:body] + ) + end +end +# rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/contacts/index.haml b/app/views/admin/contacts/index.haml index 7c27cf646..b8704a696 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| %tr %td= link_to(x, admin_contact_path(x)) - %td= x.code %td= x.ident %td= x.email + %td= x.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..eec7e6145 100644 --- a/app/views/admin/contacts/partials/_address.haml +++ b/app/views/admin/contacts/partials/_address.haml @@ -4,25 +4,17 @@ .panel-body - unless @contact.address.nil? %dl.dl-horizontal - %dt= t('country') - %dd= @contact.address.country + %dt= t('street') + %dd= @contact.street %dt= t('city') - %dd= @contact.address.city - - %dt= t('street') - %dd= @contact.address.street - - - if @contact.address.street2 - %dt= t('street') - %dd= @contact.address.street2 - - - if @contact.address.street3 - %dt= t('street') - %dd= @contact.address.street3 - - + %dd= @contact.city %dt= t('zip') - %dd= @contact.address.zip + %dd= @contact.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..512e7cd5d 100644 --- a/app/views/admin/contacts/partials/_general.haml +++ b/app/views/admin/contacts/partials/_general.haml @@ -3,30 +3,28 @@ %h3.panel-title= t('general') .panel-body %dl.dl-horizontal - %dt= t('name') - %dd= @contact.name + %dt= t(:ident) + %dd= @contact.ident + ' [' + @contact.ident_type + ']' - %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 + %dt= t(:org_name) + %dd= @contact.org_name + - 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..8a3e3095a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -1,7 +1,7 @@ .row .col-sm-12 %h2.text-center-xs - = "#{t('contact_details')}" + = @contact.name %hr .row .col-md-6= render 'admin/contacts/partials/general' 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..64e195c04 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: @@ -259,7 +238,6 @@ en: unimplemented_command: 'Unimplemented command' domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar' - code: 'Code' value: 'Value' action: 'Action' @@ -321,7 +299,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 +308,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 +479,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/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 62de51c5a..da6295c6f 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -1,451 +1,411 @@ -# 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) + 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' } } + } + create_xml = @epp_xml.create(defaults.deep_merge(overwrites)) + 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][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 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' + + 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)) + 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].count.should == 3 + 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)) + 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].count.should == 2 + 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 + before :all do + @registrar1_contact = Fabricate( + :contact, code: 'info-4444', registrar: @registrar1, + name: 'Johnny Awesome', address: Fabricate(:address)) + end + + 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 + 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..90f574fab 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -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 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..d05d0d40c 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 @@ -179,57 +179,18 @@ describe Contact do 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 + # @contact.creator_str.should == 'gitlab' end - # TODO: change up_id to something else it 'should return username of updater' do - # @contact.up_id.should == 'gitlab' + # @contact.updator.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 From 18c80fab235b2100c84604491726a734c93cf136 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 02:21:23 +0200 Subject: [PATCH 08/31] Merge updates and fixes --- app/controllers/application_controller.rb | 8 -------- app/controllers/epp/domains_controller.rb | 2 +- app/models/ability.rb | 8 +++++++- db/schema.rb | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 698cbfa88..047104c6b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,14 +15,6 @@ class ApplicationController < ActionController::Base redirect_to admin_dashboard_path, alert: exception.message end - def current_ability - if defined?(current_api_user) && current_api_user.present? - current_api_user.ability - else - current_user.ability - end - end - def after_sign_in_path_for(_resource) if session[:user_return_to] && session[:user_return_to] != login_path return session[:user_return_to].to_s diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index eac99120e..4530fc180 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -208,7 +208,7 @@ class Epp::DomainsController < EppController return domain if domain.auth_info == params[:parsed_frame].css('authInfo pw').text - if (domain.registrar != current_api_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/models/ability.rb b/app/models/ability.rb index 9980e9709..666ea6685 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -5,7 +5,13 @@ class Ability alias_action :show, :create, :update, :destroy, to: :crud @user = user || AdminUser.new - @user.roles.each { |role| send(role) } if @user.roles + + case @user.class.to_s + when 'AdminUser' + @user.roles.each { |role| send(role) } if @user.roles + when 'ApiUser' + epp + end can :show, :dashboard end diff --git a/db/schema.rb b/db/schema.rb index c963f2c75..6a889a676 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: 20150212125339) do +ActiveRecord::Schema.define(version: 20150213104014) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From e098547dee4f310fc0622b614f1bd701f9ab46a3 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 09:11:32 +0200 Subject: [PATCH 09/31] Added legal document to contact, removed old comments --- app/controllers/epp/contacts_controller.rb | 6 + app/models/contact.rb | 3 +- app/models/domain.rb | 107 +++++------------- app/models/epp/contact.rb | 36 +++--- app/views/admin/contacts/show.haml | 3 + .../domains/partials/_legal_documents.haml | 2 +- app/views/admin/domains/show.haml | 3 +- spec/epp/contact_spec.rb | 28 ++++- 8 files changed, 78 insertions(+), 110 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index c25050aad..277ba16ec 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -94,6 +94,8 @@ class Epp::ContactsController < EppController 'postalInfo > name', 'postalInfo > addr > city', 'postalInfo > addr > cc', 'ident', 'voice', 'email' ) + @prefix = nil + requires 'extension > extdata > legalDocument' end def validate_update @@ -105,10 +107,14 @@ class Epp::ContactsController < EppController } end requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end def validate_delete @prefix = 'delete > delete >' requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end end diff --git a/app/models/contact.rb b/app/models/contact.rb index 539c0d1a2..ae47e880b 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -7,10 +7,11 @@ 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 diff --git a/app/models/domain.rb b/app/models/domain.rb index ee70421f0..1453f55f0 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! @@ -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 index bb0af23cd..fa11c2663 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -8,6 +8,7 @@ class Epp::Contact < Contact class << self # rubocop: disable Metrics/PerceivedComplexity # rubocop: disable Metrics/CyclomaticComplexity + # rubocop: disable Metrics/MethodLength def attrs_from(frame) f = frame at = {}.with_indifferent_access @@ -23,16 +24,28 @@ class Epp::Contact < Contact at[:ident_type] = f.css('ident').attr('type').text end - at[:address_attributes] = {} + 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[: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? + + legald = f.css('legalDocument').first + if legald.present? + at[:legal_documents_attributes] = {}.with_indifferent_access + lat = at[:legal_documents_attributes] + lat[0] = {}.with_indifferent_access + lat[0][:document_type] = legald['type'] + lat[0][:body] = legald.text + at.delete(:legal_documents_attributes) if at[:legal_documents_attributes].blank? + end + at end + # rubocop: enable Metrics/MethodLength # rubocop: enable Metrics/PerceivedComplexity # rubocop: enable Metrics/CyclomaticComplexity @@ -40,16 +53,6 @@ class Epp::Contact < Contact return super if frame.blank? super(attrs_from(frame)) end - - def parse_legal_document_from_frame(parsed_frame) - ld = parsed_frame.css('legalDocument').first - return nil unless ld - - { - body: ld.text, - type: ld['type'] - } - end end def epp_code_map # rubocop:disable Metrics/MethodLength @@ -74,14 +77,5 @@ class Epp::Contact < Contact at.deep_merge!(self.class.attrs_from(frame.css('chg'))) super(at) end - - def attach_legal_document(legal_document_data) - return unless legal_document_data - - legal_documents.build( - document_type: legal_document_data[:type], - body: legal_document_data[:body] - ) - end end # rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/contacts/show.haml b/app/views/admin/contacts/show.haml index 8a3e3095a..dba7dd39a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -8,3 +8,6 @@ .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/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index da6295c6f..d6bb893d6 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -17,6 +17,12 @@ describe 'EPP Contact', epp: true do 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 @@ -40,7 +46,7 @@ describe 'EPP Contact', epp: true do email: { value: 'test@example.example' }, ident: { value: '37605030299', attrs: { type: 'priv' } } } - create_xml = @epp_xml.create(defaults.deep_merge(overwrites)) + create_xml = @epp_xml.create(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(create_xml, :xml) end @@ -58,6 +64,8 @@ describe 'EPP Contact', epp: true do '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' @@ -65,8 +73,9 @@ describe 'EPP Contact', epp: true do 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 == 6 + response[:results].count.should == 7 end it 'successfully creates a contact' do @@ -81,6 +90,7 @@ describe 'EPP Contact', epp: true do @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' @@ -156,7 +166,7 @@ describe 'EPP Contact', epp: true do } } } - update_xml = @epp_xml.update(defaults.deep_merge(overwrites)) + update_xml = @epp_xml.update(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(update_xml, :xml) end @@ -172,7 +182,10 @@ describe 'EPP Contact', epp: true do response[:results][2][:msg].should == 'Required parameter missing: update > update > authInfo > pw' response[:results][2][:result_code].should == '2003' - response[:results].count.should == 3 + 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 @@ -225,7 +238,7 @@ describe 'EPP Contact', epp: true do id: { value: @contact.code }, authInfo: { pw: { value: @contact.auth_info } } } - delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites)) + delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(delete_xml, :xml) end @@ -238,7 +251,10 @@ describe 'EPP Contact', epp: true do response[:results][1][:msg].should == 'Required parameter missing: delete > delete > authInfo > pw' response[:results][1][:result_code].should == '2003' - response[:results].count.should == 2 + 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 From 015a65d7c64e45f7960967b506f767a59bff3a3f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 10:32:40 +0200 Subject: [PATCH 10/31] Legaldocument for contact update --- app/models/epp/contact.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index fa11c2663..62a052077 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -33,14 +33,9 @@ class Epp::Contact < Contact 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? - legald = f.css('legalDocument').first - if legald.present? - at[:legal_documents_attributes] = {}.with_indifferent_access - lat = at[:legal_documents_attributes] - lat[0] = {}.with_indifferent_access - lat[0][:document_type] = legald['type'] - lat[0][:body] = legald.text - at.delete(:legal_documents_attributes) if at[:legal_documents_attributes].blank? + legal_frame = f.css('legalDocument').first + if legal_frame.present? + at[:legal_documents_attributes] = legal_document_attrs(legal_frame) end at @@ -53,6 +48,14 @@ class Epp::Contact < Contact 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][:document_type] = legal_frame['type'] + attrs[0][:body] = legal_frame + attrs + end end def epp_code_map # rubocop:disable Metrics/MethodLength @@ -75,6 +78,9 @@ class Epp::Contact < Contact 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 From 41bb72e04374a3f125dfdefdfae0f67fd5014f5c Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Tue, 17 Feb 2015 10:42:12 +0200 Subject: [PATCH 11/31] Revert user cert --- CHANGELOG.md | 91 ----------------------------- app/models/api_user.rb | 2 +- app/views/admin/api_users/show.haml | 8 +-- 3 files changed, 5 insertions(+), 96 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f0cc390..a742e960c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,94 +1,3 @@ -12.02.2015 - -Go to registry shared folder and setup CA directory tree: -``` -mkdir ca -cd ca -mkdir certs crl newcerts private -chmod 700 private -touch index.txt -echo 1000 > serial -``` - -Generate the root key (prompts for pass phrase): -``` -openssl genrsa -aes256 -out private/ca.key.pem 4096 -``` - -Configure OpenSSL: -``` -sudo su - -cd /etc/ssl/ -cp openssl.cnf openssl.cnf.bak -nano openssl.cnf -exit -``` - -Make sure the following options are in place: -``` -[ CA_default ] -# Where everything is kept -dir = /home/registry/registry/shared/ca - -[ usr_cert ] -# These extensions are added when 'ca' signs a request. -basicConstraints=CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment -nsComment = "OpenSSL Generated Certificate" -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -[ v3_ca ] -# Extensions for a typical CA -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:always,issuer -basicConstraints = CA:true -keyUsage = cRLSign, keyCertSign - -[ policy_match ] -countryName = optional -stateOrProvinceName = optional -organizationName = optional -organizationalUnitName = optional -commonName = optional -emailAddress = optional -``` - -Issue the root certificate (prompts for additional data): -``` -openssl req -new -x509 -days 3650 -key private/ca.key.pem -sha256 -extensions v3_ca -out certs/ca.cert.pem -chmod 444 certs/ca.cert.pem -``` - -Configure EPP virtual host: -``` -sudo nano /etc/apache2/sites-enabled/epp.conf -``` - -Replace this line: -``` -SSLVerifyClient optional_no_ca -``` - -With these lines: -``` - SSLVerifyClient require - SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem -``` - -Reload apache: -``` -sudo /etc/init.d/apache2 reload -``` - -Configure application.yml to match the CA settings: -``` -ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.cert.pem' -ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' -ca_key_password: 'registryalpha' -``` - 20.01.2015 * Added dedicated mina cron:setup and mina cron:clear for manual cron management. diff --git a/app/models/api_user.rb b/app/models/api_user.rb index fe0368125..a067d00f1 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -9,7 +9,7 @@ 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 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 - From dd1063c8f7e62f88056ef8d598b39bb889c8f95f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:48:02 +0200 Subject: [PATCH 12/31] Fixed seeds for registrar updates --- db/schema.rb | 1 + db/seeds.rb | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 614fe9ae6..6a889a676 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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| 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! From 2f74b47249891927f2bb446ddec942ce0ad29a70 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:48:02 +0200 Subject: [PATCH 13/31] Fixed seeds for registrar updates --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index 6a889a676..c963f2c75 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: 20150212125339) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From fa76d9079e0269481202ca80b63d3ad474347a16 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:53:38 +0200 Subject: [PATCH 14/31] Epp doc syntax fixes --- doc/epp/contact.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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) From f96398ea9fa1b3c7b03c5f3574a16973a005e3c5 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:55:04 +0200 Subject: [PATCH 15/31] Allow blank for requires helper --- app/controllers/epp_controller.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/epp_controller.rb b/app/controllers/epp_controller.rb index 28eff040f..4749c1ff0 100644 --- a/app/controllers/epp_controller.rb +++ b/app/controllers/epp_controller.rb @@ -84,12 +84,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) From b38f195af645af7b47040b4a15ec9a66331eb4d4 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 10 Feb 2015 14:55:21 +0200 Subject: [PATCH 16/31] Added new requires format to contacts controller --- app/controllers/epp/contacts_controller.rb | 61 ++++++++++------------ app/helpers/whodunnit_helper.rb | 25 --------- 2 files changed, 28 insertions(+), 58 deletions(-) delete mode 100644 app/helpers/whodunnit_helper.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 6aa5efce3..fefdce438 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,5 +1,14 @@ class Epp::ContactsController < EppController - helper WhodunnitHelper ## Refactor this? + 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 + render_epp_response 'epp/contacts/info' + end def create @contact = Contact.new(contact_and_address_attributes) @@ -39,17 +48,6 @@ class Epp::ContactsController < EppController render_epp_response '/epp/contacts/check' end - 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 - render_epp_response 'epp/contacts/info' - end - def renew epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') } handle_errors @@ -61,20 +59,22 @@ class Epp::ContactsController < EppController ## 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? + @prefix = 'create > create >' + requires 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' 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']]) + def validate_update + @prefix = 'update > update >' + requires 'id' + + 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 end def contact_exists?(code) @@ -83,13 +83,6 @@ class Epp::ContactsController < EppController 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'] @@ -115,11 +108,13 @@ class Epp::ContactsController < EppController ## SHARED def find_contact - contact = Contact.find_by(code: @ph[:id]) - unless contact + contact_code = params[:parsed_frame].css('id').text.strip.downcase + contact = Contact.find_by(code: contact_code) + + if contact.blank? epp_errors << { code: '2303', msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: @ph[:id] } } + value: { obj: 'id', val: contact_code } } end contact 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 From 62f8061e100142c6071782f79460e5a952b72b2d Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 12 Feb 2015 12:56:13 +0200 Subject: [PATCH 17/31] Rubocop syntax fix --- app/controllers/epp/domains_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 26eeb7eb6..319c1c041 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -206,7 +206,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_api_user.registrar) && secure[:secure] == true epp_errors << { code: '2302', msg: I18n.t('errors.messages.domain_exists_but_belongs_to_other_registrar'), From 96d1c60dd8c012837416cdbbeaf6b9904d191be9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:38:36 +0200 Subject: [PATCH 18/31] Refactored contact with ability --- Guardfile | 10 +- app/controllers/admin_controller.rb | 1 - app/controllers/application_controller.rb | 24 +- app/controllers/epp/contacts_controller.rb | 235 ++--- app/controllers/epp/domains_controller.rb | 2 + app/controllers/epp/errors_controller.rb | 2 + app/controllers/epp/keyrelays_controller.rb | 2 + app/controllers/epp/polls_controller.rb | 4 +- app/controllers/epp/sessions_controller.rb | 2 + app/controllers/epp_controller.rb | 17 +- app/controllers/sessions_controller.rb | 2 + app/models/ability.rb | 15 +- app/models/api_user.rb | 5 + app/models/concerns/version_session.rb | 2 +- app/models/concerns/versions.rb | 8 +- app/models/contact.rb | 139 +-- app/models/epp/contact.rb | 87 ++ app/views/admin/contacts/index.haml | 10 +- .../admin/contacts/partials/_address.haml | 26 +- .../admin/contacts/partials/_general.haml | 30 +- app/views/admin/contacts/show.haml | 2 +- .../epp/contacts/_postal_info.xml.builder | 18 +- app/views/epp/contacts/check.xml.builder | 7 +- app/views/epp/contacts/info.xml.builder | 19 +- config/locales/en.yml | 29 +- .../20150212125339_add_state_to_address.rb | 5 + spec/epp/contact_spec.rb | 862 +++++++++--------- spec/epp/domain_spec.rb | 14 +- spec/fabricators/domain_fabricator.rb | 2 +- spec/models/contact_disclosure_spec.rb | 91 -- spec/models/contact_spec.rb | 47 +- spec/support/epp.rb | 2 +- spec/support/epp_contact_xml_helper.rb | 87 -- 33 files changed, 763 insertions(+), 1045 deletions(-) create mode 100644 app/models/epp/contact.rb create mode 100644 db/migrate/20150212125339_add_state_to_address.rb delete mode 100644 spec/models/contact_disclosure_spec.rb delete mode 100644 spec/support/epp_contact_xml_helper.rb diff --git a/Guardfile b/Guardfile index eab74ee21..955ca3b4e 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$}) 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..698cbfa88 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,6 @@ class ApplicationController < ActionController::Base + check_authorization + # 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,22 @@ 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 current_ability + if defined?(current_api_user) && current_api_user.present? + current_api_user.ability + else + current_user.ability + end + 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 +50,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 fefdce438..c25050aad 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,183 +1,114 @@ class Epp::ContactsController < EppController + 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 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]) + 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 - @prefix = 'create > create >' - requires 'postalInfo > name', 'postalInfo > addr > city', - 'postalInfo > addr > cc', 'ident', 'voice', 'email' + def find_password + @password = params[:parsed_frame].css('authInfo pw').text + end + + def find_contact + 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 + end + + # + # Validations + # + def validate_info + @prefix = 'info > info >' + requires 'id' + end + + def validate_check + @prefix = 'check > check >' + requires 'id' + end + + def validate_create + @prefix = 'create > create >' + requires( + 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' + ) end - ## UPDATE def validate_update @prefix = 'update > update >' - requires 'id' - 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' 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 - - ## 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_code = params[:parsed_frame].css('id').text.strip.downcase - contact = Contact.find_by(code: contact_code) - - if contact.blank? - epp_errors << { code: '2303', - msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: contact_code } } - end - 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 - 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 - end - - def update_rights? - pw = @ph.try(:[], :authInfo).try(:[], :pw) - return true if pw && @contact.auth_info_matches(pw) - epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') } - false - end - - def contact_and_address_attributes(type = :create) - case type - when :update - # TODO: support for rem/add - contact_hash = merge_attribute_hash(@ph[:chg], type).delete_if { |_k, v| v.empty? } - else - contact_hash = merge_attribute_hash(@ph, type) - end - contact_hash[:ident_type] = ident_type unless ident_type.nil? - contact_hash - end - - def merge_attribute_hash(prms, type) - contact_hash = Contact.extract_attributes(prms, type) - contact_hash = contact_hash.merge( - Address.extract_attributes((prms.try(:[], :postalInfo) || [])) - ) - contact_hash[:disclosure_attributes] = - ContactDisclosure.extract_attributes(params[:parsed_frame]) - - contact_hash - 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 - end - - def validate_params - return true if @ph - epp_errors << { code: '2001', msg: t(:'errors.messages.epp_command_syntax_error') } - false + @prefix = 'delete > delete >' + requires 'id', 'authInfo > pw' end end diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 319c1c041..eac99120e 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) 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 1485001a8..21161c7a0 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 4749c1ff0..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))}" @@ -112,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..7e1247c1a 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,6 @@ class SessionsController < Devise::SessionsController + skip_authorization_check only: [:login, :create] + def create # TODO: Create ID Card login here: # this is just testing config diff --git a/app/models/ability.rb b/app/models/ability.rb index 47fc6c209..9980e9709 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,16 +2,25 @@ 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? - 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 a067d00f1..aee1ff203 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -13,6 +13,11 @@ class ApiUser < User 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..539c0d1a2 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 @@ -19,25 +18,20 @@ class Contact < ActiveRecord::Base 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 :code, uniqueness: { message: :epp_id_taken } - 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_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' @@ -54,6 +48,32 @@ class Contact < ActiveRecord::Base CONTACT_TYPE_ADMIN = 'admin' CONTACT_TYPES = [CONTACT_TYPE_TECH, CONTACT_TYPE_ADMIN] + 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_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 @@ -66,15 +86,6 @@ class Contact < ActiveRecord::Base 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 end @@ -83,18 +94,6 @@ class Contact < ActiveRecord::Base 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 - end - # generate random id for contact def generate_code self.code = SecureRandom.hex(4) @@ -114,6 +113,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? @@ -122,76 +123,4 @@ class Contact < ActiveRecord::Base end 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})" } } - end - end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb new file mode 100644 index 000000000..bb0af23cd --- /dev/null +++ b/app/models/epp/contact.rb @@ -0,0 +1,87 @@ +# rubocop: disable Metrics/ClassLength +class Epp::Contact < Contact + include EppErrors + + # disable STI, there is type column present + self.inheritance_column = :sti_disabled + + class << self + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity + 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').text + end + + at[:address_attributes] = {} + 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? + at + end + # rubocop: enable Metrics/PerceivedComplexity + # rubocop: enable Metrics/CyclomaticComplexity + + def new(frame) + return super if frame.blank? + super(attrs_from(frame)) + end + + def parse_legal_document_from_frame(parsed_frame) + ld = parsed_frame.css('legalDocument').first + return nil unless ld + + { + body: ld.text, + type: ld['type'] + } + 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] + ] + } + end + + def update_attributes(frame) + return super if frame.blank? + at = {}.with_indifferent_access + at.deep_merge!(self.class.attrs_from(frame.css('chg'))) + super(at) + end + + def attach_legal_document(legal_document_data) + return unless legal_document_data + + legal_documents.build( + document_type: legal_document_data[:type], + body: legal_document_data[:body] + ) + end +end +# rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/contacts/index.haml b/app/views/admin/contacts/index.haml index 7c27cf646..b8704a696 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| %tr %td= link_to(x, admin_contact_path(x)) - %td= x.code %td= x.ident %td= x.email + %td= x.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..eec7e6145 100644 --- a/app/views/admin/contacts/partials/_address.haml +++ b/app/views/admin/contacts/partials/_address.haml @@ -4,25 +4,17 @@ .panel-body - unless @contact.address.nil? %dl.dl-horizontal - %dt= t('country') - %dd= @contact.address.country + %dt= t('street') + %dd= @contact.street %dt= t('city') - %dd= @contact.address.city - - %dt= t('street') - %dd= @contact.address.street - - - if @contact.address.street2 - %dt= t('street') - %dd= @contact.address.street2 - - - if @contact.address.street3 - %dt= t('street') - %dd= @contact.address.street3 - - + %dd= @contact.city %dt= t('zip') - %dd= @contact.address.zip + %dd= @contact.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..512e7cd5d 100644 --- a/app/views/admin/contacts/partials/_general.haml +++ b/app/views/admin/contacts/partials/_general.haml @@ -3,30 +3,28 @@ %h3.panel-title= t('general') .panel-body %dl.dl-horizontal - %dt= t('name') - %dd= @contact.name + %dt= t(:ident) + %dd= @contact.ident + ' [' + @contact.ident_type + ']' - %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 + %dt= t(:org_name) + %dd= @contact.org_name + - 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..8a3e3095a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -1,7 +1,7 @@ .row .col-sm-12 %h2.text-center-xs - = "#{t('contact_details')}" + = @contact.name %hr .row .col-md-6= render 'admin/contacts/partials/general' 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..64e195c04 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: @@ -259,7 +238,6 @@ en: unimplemented_command: 'Unimplemented command' domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar' - code: 'Code' value: 'Value' action: 'Action' @@ -321,7 +299,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 +308,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 +479,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/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 62de51c5a..da6295c6f 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -1,451 +1,411 @@ -# 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) + 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' } } + } + create_xml = @epp_xml.create(defaults.deep_merge(overwrites)) + 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][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 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' + + 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)) + 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].count.should == 3 + 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)) + 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].count.should == 2 + 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 + before :all do + @registrar1_contact = Fabricate( + :contact, code: 'info-4444', registrar: @registrar1, + name: 'Johnny Awesome', address: Fabricate(:address)) + end + + 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 + 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..90f574fab 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -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 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..d05d0d40c 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 @@ -179,57 +179,18 @@ describe Contact do 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 + # @contact.creator_str.should == 'gitlab' end - # TODO: change up_id to something else it 'should return username of updater' do - # @contact.up_id.should == 'gitlab' + # @contact.updator.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 From 2e47bcf5cbefc891ee6ec2790209b3ba10d8536a Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 02:21:23 +0200 Subject: [PATCH 19/31] Merge updates and fixes --- app/controllers/application_controller.rb | 8 -------- app/controllers/epp/domains_controller.rb | 2 +- app/models/ability.rb | 8 +++++++- db/schema.rb | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 698cbfa88..047104c6b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,14 +15,6 @@ class ApplicationController < ActionController::Base redirect_to admin_dashboard_path, alert: exception.message end - def current_ability - if defined?(current_api_user) && current_api_user.present? - current_api_user.ability - else - current_user.ability - end - end - def after_sign_in_path_for(_resource) if session[:user_return_to] && session[:user_return_to] != login_path return session[:user_return_to].to_s diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index eac99120e..4530fc180 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -208,7 +208,7 @@ class Epp::DomainsController < EppController return domain if domain.auth_info == params[:parsed_frame].css('authInfo pw').text - if (domain.registrar != current_api_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/models/ability.rb b/app/models/ability.rb index 9980e9709..666ea6685 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -5,7 +5,13 @@ class Ability alias_action :show, :create, :update, :destroy, to: :crud @user = user || AdminUser.new - @user.roles.each { |role| send(role) } if @user.roles + + case @user.class.to_s + when 'AdminUser' + @user.roles.each { |role| send(role) } if @user.roles + when 'ApiUser' + epp + end can :show, :dashboard end diff --git a/db/schema.rb b/db/schema.rb index c963f2c75..6a889a676 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: 20150212125339) do +ActiveRecord::Schema.define(version: 20150213104014) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From ba55f3f9bcdfc042f2a17c548fb515f7117883df Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 09:11:32 +0200 Subject: [PATCH 20/31] Added legal document to contact, removed old comments --- app/controllers/epp/contacts_controller.rb | 6 + app/models/contact.rb | 3 +- app/models/domain.rb | 107 +++++------------- app/models/epp/contact.rb | 36 +++--- app/views/admin/contacts/show.haml | 3 + .../domains/partials/_legal_documents.haml | 2 +- app/views/admin/domains/show.haml | 3 +- spec/epp/contact_spec.rb | 28 ++++- 8 files changed, 78 insertions(+), 110 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index c25050aad..277ba16ec 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -94,6 +94,8 @@ class Epp::ContactsController < EppController 'postalInfo > name', 'postalInfo > addr > city', 'postalInfo > addr > cc', 'ident', 'voice', 'email' ) + @prefix = nil + requires 'extension > extdata > legalDocument' end def validate_update @@ -105,10 +107,14 @@ class Epp::ContactsController < EppController } end requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end def validate_delete @prefix = 'delete > delete >' requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end end diff --git a/app/models/contact.rb b/app/models/contact.rb index 539c0d1a2..ae47e880b 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -7,10 +7,11 @@ 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 diff --git a/app/models/domain.rb b/app/models/domain.rb index ee70421f0..1453f55f0 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! @@ -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 index bb0af23cd..fa11c2663 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -8,6 +8,7 @@ class Epp::Contact < Contact class << self # rubocop: disable Metrics/PerceivedComplexity # rubocop: disable Metrics/CyclomaticComplexity + # rubocop: disable Metrics/MethodLength def attrs_from(frame) f = frame at = {}.with_indifferent_access @@ -23,16 +24,28 @@ class Epp::Contact < Contact at[:ident_type] = f.css('ident').attr('type').text end - at[:address_attributes] = {} + 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[: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? + + legald = f.css('legalDocument').first + if legald.present? + at[:legal_documents_attributes] = {}.with_indifferent_access + lat = at[:legal_documents_attributes] + lat[0] = {}.with_indifferent_access + lat[0][:document_type] = legald['type'] + lat[0][:body] = legald.text + at.delete(:legal_documents_attributes) if at[:legal_documents_attributes].blank? + end + at end + # rubocop: enable Metrics/MethodLength # rubocop: enable Metrics/PerceivedComplexity # rubocop: enable Metrics/CyclomaticComplexity @@ -40,16 +53,6 @@ class Epp::Contact < Contact return super if frame.blank? super(attrs_from(frame)) end - - def parse_legal_document_from_frame(parsed_frame) - ld = parsed_frame.css('legalDocument').first - return nil unless ld - - { - body: ld.text, - type: ld['type'] - } - end end def epp_code_map # rubocop:disable Metrics/MethodLength @@ -74,14 +77,5 @@ class Epp::Contact < Contact at.deep_merge!(self.class.attrs_from(frame.css('chg'))) super(at) end - - def attach_legal_document(legal_document_data) - return unless legal_document_data - - legal_documents.build( - document_type: legal_document_data[:type], - body: legal_document_data[:body] - ) - end end # rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/contacts/show.haml b/app/views/admin/contacts/show.haml index 8a3e3095a..dba7dd39a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -8,3 +8,6 @@ .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/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index da6295c6f..d6bb893d6 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -17,6 +17,12 @@ describe 'EPP Contact', epp: true do 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 @@ -40,7 +46,7 @@ describe 'EPP Contact', epp: true do email: { value: 'test@example.example' }, ident: { value: '37605030299', attrs: { type: 'priv' } } } - create_xml = @epp_xml.create(defaults.deep_merge(overwrites)) + create_xml = @epp_xml.create(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(create_xml, :xml) end @@ -58,6 +64,8 @@ describe 'EPP Contact', epp: true do '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' @@ -65,8 +73,9 @@ describe 'EPP Contact', epp: true do 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 == 6 + response[:results].count.should == 7 end it 'successfully creates a contact' do @@ -81,6 +90,7 @@ describe 'EPP Contact', epp: true do @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' @@ -156,7 +166,7 @@ describe 'EPP Contact', epp: true do } } } - update_xml = @epp_xml.update(defaults.deep_merge(overwrites)) + update_xml = @epp_xml.update(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(update_xml, :xml) end @@ -172,7 +182,10 @@ describe 'EPP Contact', epp: true do response[:results][2][:msg].should == 'Required parameter missing: update > update > authInfo > pw' response[:results][2][:result_code].should == '2003' - response[:results].count.should == 3 + 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 @@ -225,7 +238,7 @@ describe 'EPP Contact', epp: true do id: { value: @contact.code }, authInfo: { pw: { value: @contact.auth_info } } } - delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites)) + delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites), @legal_document) epp_plain_request(delete_xml, :xml) end @@ -238,7 +251,10 @@ describe 'EPP Contact', epp: true do response[:results][1][:msg].should == 'Required parameter missing: delete > delete > authInfo > pw' response[:results][1][:result_code].should == '2003' - response[:results].count.should == 2 + 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 From 7c749ee1aeb28dcd9e830c68988caa46f605cc76 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 10:32:40 +0200 Subject: [PATCH 21/31] Legaldocument for contact update --- app/models/epp/contact.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index fa11c2663..62a052077 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -33,14 +33,9 @@ class Epp::Contact < Contact 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? - legald = f.css('legalDocument').first - if legald.present? - at[:legal_documents_attributes] = {}.with_indifferent_access - lat = at[:legal_documents_attributes] - lat[0] = {}.with_indifferent_access - lat[0][:document_type] = legald['type'] - lat[0][:body] = legald.text - at.delete(:legal_documents_attributes) if at[:legal_documents_attributes].blank? + legal_frame = f.css('legalDocument').first + if legal_frame.present? + at[:legal_documents_attributes] = legal_document_attrs(legal_frame) end at @@ -53,6 +48,14 @@ class Epp::Contact < Contact 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][:document_type] = legal_frame['type'] + attrs[0][:body] = legal_frame + attrs + end end def epp_code_map # rubocop:disable Metrics/MethodLength @@ -75,6 +78,9 @@ class Epp::Contact < Contact 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 From fd52433f8872d495c61fff3e468257324940e969 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 13:25:36 +0200 Subject: [PATCH 22/31] Fix logout issue --- app/controllers/sessions_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7e1247c1a..ff4f74517 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,5 @@ class SessionsController < Devise::SessionsController - skip_authorization_check only: [:login, :create] + skip_authorization_check def create # TODO: Create ID Card login here: From 645d475522296ca6cc687929b68efccd78685032 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 13:32:23 +0200 Subject: [PATCH 23/31] Don't check authorization for Devise --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 047104c6b..8840e4989 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,5 @@ class ApplicationController < ActionController::Base - check_authorization + check_authorization unless: :devise_controller? # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. From 915ac3162f9ab39ae71a7a43eaa6b98731fbf5c9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 15:40:20 +0200 Subject: [PATCH 24/31] Migration: added contact code index and ident country code --- db/migrate/20150217133755_add_country_code_ident.rb | 5 +++++ db/migrate/20150217133937_add_index_for_contact_code.rb | 5 +++++ db/schema.rb | 5 ++++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150217133755_add_country_code_ident.rb create mode 100644 db/migrate/20150217133937_add_index_for_contact_code.rb 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 6a889a676..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" @@ -96,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" From 8082bf60b8bfea9b4073f8eb0abf6ee59c3624a3 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 15:54:41 +0200 Subject: [PATCH 25/31] Epp::models changes should trigger Epp tests in guard --- Guardfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Guardfile b/Guardfile index 955ca3b4e..759359964 100644 --- a/Guardfile +++ b/Guardfile @@ -18,6 +18,7 @@ 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/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" } From e9b77c5b0f0941cb54f698bb8134cbd3fc114639 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 15:26:21 +0200 Subject: [PATCH 26/31] Trigger guard tests for epp controller changes --- Guardfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Guardfile b/Guardfile index 759359964..e6d0a443a 100644 --- a/Guardfile +++ b/Guardfile @@ -18,6 +18,7 @@ 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" } From 44f11de5c88fc4377a34ebab8c96f2de314ffa47 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 15:26:51 +0200 Subject: [PATCH 27/31] Added ident country code and improved other ident things --- app/controllers/epp/contacts_controller.rb | 7 ++ app/helpers/application_helper.rb | 9 +++ app/models/contact.rb | 44 ++++++++----- app/models/domain.rb | 2 +- app/models/epp/contact.rb | 8 ++- app/models/epp/epp_domain.rb | 2 +- .../admin/contacts/partials/_address.haml | 16 +++-- .../admin/contacts/partials/_general.haml | 5 +- config/locales/en.yml | 3 +- spec/epp/contact_spec.rb | 12 ++-- spec/epp/domain_spec.rb | 6 +- spec/fabricators/contact_fabricator.rb | 5 +- spec/models/contact_spec.rb | 64 ++++++++++++------- 13 files changed, 117 insertions(+), 66 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 277ba16ec..5af05d9fd 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -94,6 +94,13 @@ class Epp::ContactsController < EppController 'postalInfo > name', 'postalInfo > addr > city', 'postalInfo > addr > cc', 'ident', 'voice', 'email' ) + 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 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/models/contact.rb b/app/models/contact.rb index ae47e880b..37b562ff6 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -19,8 +19,9 @@ class Contact < ActiveRecord::Base 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_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 :street, to: :address delegate :city, to: :address @@ -29,19 +30,17 @@ class Contact < ActiveRecord::Base delegate :country_code, to: :address delegate :country, to: :address + before_validation :set_ident_country_code before_create :generate_code before_create :generate_auth_info after_create :ensure_disclosure 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 ] @@ -75,24 +74,27 @@ class Contact < ActiveRecord::Base name end - 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? + def ident_valid_format? + case ident_type + when 'priv' + case ident_country_code + when 'EE' + code = Isikukood.new(ident) + errors.add(:ident, :not_valid_EE_identity_format) unless code.valid? + end + end end def ensure_disclosure create_disclosure! unless disclosure end - def juridical? - ident_type == IDENT_TYPE_ICO + def bic? + ident_type == IDENT_TYPE_BIC end - def citizen? - ident_type != IDENT_TYPE_ICO + def priv? + ident_type != IDENT_TYPE_BIC end # generate random id for contact @@ -124,4 +126,14 @@ class Contact < ActiveRecord::Base end destroy end + + 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 1453f55f0..2d0a96673 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -211,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 diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 62a052077..b2184eba8 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -20,8 +20,9 @@ class Epp::Contact < Contact 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').text + 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 @@ -69,7 +70,8 @@ class Epp::Contact < Contact '2005' => [ # Value syntax error [:phone, :invalid], [:email, :invalid], - [:ident, :invalid] + [:ident, :invalid], + [:ident, :not_valid_EE_identity_format] ] } end 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/contacts/partials/_address.haml b/app/views/admin/contacts/partials/_address.haml index eec7e6145..aecba9a70 100644 --- a/app/views/admin/contacts/partials/_address.haml +++ b/app/views/admin/contacts/partials/_address.haml @@ -1,20 +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('street') + - if @contact.bic? + %dt= t(:org_name) + %dd= @contact.org_name + + %dt= t(:street) %dd= @contact.street - %dt= t('city') + %dt= t(:city) %dd= @contact.city - %dt= t('zip') + %dt= t(:zip) %dd= @contact.zip - %dt= t('state') + %dt= t(:state) %dd= @contact.state - %dt= t('country') + %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 512e7cd5d..2911ab783 100644 --- a/app/views/admin/contacts/partials/_general.haml +++ b/app/views/admin/contacts/partials/_general.haml @@ -4,7 +4,7 @@ .panel-body %dl.dl-horizontal %dt= t(:ident) - %dd= @contact.ident + ' [' + @contact.ident_type + ']' + %dd= "#{@contact.ident} #{ident_indicator(@contact)}" %br @@ -14,9 +14,6 @@ %dt= t(:phone) %dd= @contact.phone - %dt= t(:org_name) - %dd= @contact.org_name - - if @contact.fax %dt= t(:fax) %dd= @contact.fax diff --git a/config/locales/en.yml b/config/locales/en.yml index 64e195c04..ea38733b2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -31,7 +31,6 @@ en: activerecord: errors: models: - contact: attributes: code: @@ -46,6 +45,7 @@ en: invalid: "Email is invalid" ident: blank: "Required parameter missing - ident" + not_valid_EE_identity_format: "Ident not in valid Estonian identity format." domains: exist: 'Object association prohibits operation' @@ -237,6 +237,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' diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index d6bb893d6..a025197c6 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -44,7 +44,7 @@ describe 'EPP Contact', epp: true do }, voice: { value: '+372.1234567' }, email: { value: 'test@example.example' }, - ident: { value: '37605030299', attrs: { type: 'priv' } } + 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) @@ -331,12 +331,6 @@ describe 'EPP Contact', epp: true do end context 'info command' do - before :all do - @registrar1_contact = Fabricate( - :contact, code: 'info-4444', registrar: @registrar1, - name: 'Johnny Awesome', address: Fabricate(:address)) - end - def info_request(overwrites = {}) defaults = { id: { value: @contact.code }, @@ -363,6 +357,10 @@ describe 'EPP Contact', epp: true do 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' diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 90f574fab..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 } @@ -669,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 @@ -686,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/models/contact_spec.rb b/spec/models/contact_spec.rb index d05d0d40c..0b00e8737 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -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 @@ -177,16 +207,6 @@ describe Contact do @contact.auth_info.should == 'qwe321' end end - - context 'with creator' do - it 'should return username of creator' do - # @contact.creator_str.should == 'gitlab' - end - - it 'should return username of updater' do - # @contact.updator.should == 'gitlab' - end - end end end end From 61ee8ef9f55687f1e12a4433a0034866df6c45db Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 16:04:04 +0200 Subject: [PATCH 28/31] Rubocop yml update --- .rubocop.yml | 4 ++++ 1 file changed, 4 insertions(+) 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 From a8e43dd6cc4755eab9d43463bbb59c62a5587ba2 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 16:05:57 +0200 Subject: [PATCH 29/31] Contact got correct birthday and country code epp errors --- app/models/contact.rb | 6 ++++-- app/models/epp/contact.rb | 3 ++- app/views/admin/contacts/index.haml | 10 +++++----- config/locales/en.yml | 3 ++- spec/models/contact_spec.rb | 3 ++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/models/contact.rb b/app/models/contact.rb index 37b562ff6..722e4de33 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -18,7 +18,9 @@ class Contact < ActiveRecord::Base # 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' } + 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? @@ -80,7 +82,7 @@ class Contact < ActiveRecord::Base case ident_country_code when 'EE' code = Isikukood.new(ident) - errors.add(:ident, :not_valid_EE_identity_format) unless code.valid? + errors.add(:ident, :invalid_EE_identity_format) unless code.valid? end end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index b2184eba8..57eccbf2c 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -71,7 +71,8 @@ class Epp::Contact < Contact [:phone, :invalid], [:email, :invalid], [:ident, :invalid], - [:ident, :not_valid_EE_identity_format] + [:ident, :invalid_EE_identity_format], + [:ident, :invalid_birthday_format] ] } end diff --git a/app/views/admin/contacts/index.haml b/app/views/admin/contacts/index.haml index b8704a696..3f437d62d 100644 --- a/app/views/admin/contacts/index.haml +++ b/app/views/admin/contacts/index.haml @@ -30,12 +30,12 @@ %th{class: 'col-xs-2'} = 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.ident - %td= x.email - %td= x.code + %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/config/locales/en.yml b/config/locales/en.yml index ea38733b2..3adea8723 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -45,7 +45,8 @@ en: invalid: "Email is invalid" ident: blank: "Required parameter missing - ident" - not_valid_EE_identity_format: "Ident not in valid Estonian identity format." + 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' diff --git a/spec/models/contact_spec.rb b/spec/models/contact_spec.rb index 0b00e8737..d605e9781 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -149,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 From e52a79a8136090b4f8c7e7d314ef162515d24654 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 16:21:34 +0200 Subject: [PATCH 30/31] Fixed contact legal document parsing --- app/models/epp/contact.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 57eccbf2c..d9d9754bf 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -53,8 +53,8 @@ class Epp::Contact < Contact 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[0][:body] = legal_frame attrs end end From 7728a90cf44690c0b01b83af494df3e44c1cabbf Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 17:24:28 +0200 Subject: [PATCH 31/31] Trying to fix documentable issue --- app/models/epp/contact.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index d9d9754bf..276b490d6 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -5,6 +5,9 @@ class Epp::Contact < Contact # 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