From 6e2eaf6fff2bd63fc82ff29e2ad2b3b976da5c88 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 01:48:02 +0200 Subject: [PATCH 01/97] 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/97] 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/97] 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/97] 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/97] 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/97] 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/97] 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/97] 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/97] 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 34a1bde3e4c771a44b8e0aac279e44717ec23390 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Tue, 17 Feb 2015 10:28:55 +0200 Subject: [PATCH 10/97] Add pki login logic --- app/controllers/epp/sessions_controller.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 1485001a8..94ee568b0 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -4,7 +4,12 @@ class Epp::SessionsController < EppController end def login - @api_user = ApiUser.find_by(login_params) + # pki login + if request.env['HTTP_SSL_CLIENT_S_DN_CN'] == login_params[:username] + @api_user = ApiUser.find_by(username: login_params[:username]) + else + @api_user = ApiUser.find_by(login_params) + end if @api_user.try(:active) epp_session[:api_user_id] = @api_user.id From 015a65d7c64e45f7960967b506f767a59bff3a3f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 17 Feb 2015 10:32:40 +0200 Subject: [PATCH 11/97] 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 12/97] 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 13/97] 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 14/97] 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 15/97] 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 16/97] 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 17/97] 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 18/97] 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 19/97] 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 20/97] 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 21/97] 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 22/97] 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 23/97] 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 24/97] 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 25/97] 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 26/97] 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 27/97] 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 28/97] 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 29/97] 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 30/97] 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 31/97] 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 32/97] 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 From e27cda13909eb15a58e682c1d1782ba85430313c Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 18 Feb 2015 18:06:11 +0200 Subject: [PATCH 33/97] refactor legal document attrs --- app/models/epp/contact.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 276b490d6..0a6c14020 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -5,9 +5,6 @@ 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 @@ -54,11 +51,10 @@ class Epp::Contact < Contact end def legal_document_attrs(legal_frame) - attrs = {}.with_indifferent_access - attrs[0] = {}.with_indifferent_access - attrs[0][:body] = legal_frame.text - attrs[0][:document_type] = legal_frame['type'] - attrs + [{ + body: legal_frame.text, + document_type: legal_frame['type'] + }] end end From 316f9376fe375619d354c77817ab69c8c68ef032 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 14:44:42 +0200 Subject: [PATCH 34/97] Allow logging in with only username --- app/api/repp/api.rb | 14 ++++++++++++-- app/controllers/epp/sessions_controller.rb | 4 ++-- config/routes.rb | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb index 04b805597..ec0b3167f 100644 --- a/app/api/repp/api.rb +++ b/app/api/repp/api.rb @@ -3,8 +3,18 @@ module Repp format :json prefix :repp - http_basic do |username, password| - @current_user ||= ApiUser.find_by(username: username, password: password) + before do + auth_param = request.headers['Authorization'].split(' ', 2).second + username, password = ::Base64.decode64(auth_param || '').split(':', 2) + + # allow user lookup only by username if request came from webclient + if request.ip == APP_CONFIG['webclient_ip'] && password.blank? + login_params = { username: username } + else + login_params = { username: username, password: password } + end + + @current_user ||= ApiUser.find_by(login_params) end helpers do diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 94ee568b0..4d7e2481b 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -4,8 +4,8 @@ class Epp::SessionsController < EppController end def login - # pki login - if request.env['HTTP_SSL_CLIENT_S_DN_CN'] == login_params[:username] + # Allow login with only username + if request.ip == APP_CONFIG['webclient_ip'] && login_params[:password].nil? @api_user = ApiUser.find_by(username: login_params[:username]) else @api_user = ApiUser.find_by(login_params) diff --git a/config/routes.rb b/config/routes.rb index cd9f85b81..e2bb835b3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,7 @@ require 'epp_constraint' Rails.application.routes.draw do namespace(:epp, defaults: { format: :xml }) do match 'session/:action', controller: 'sessions', via: :all + match 'session/pki/:action', controller: 'sessions', via: :all post 'command/:action', controller: 'domains', constraints: EppConstraint.new(:domain) post 'command/:action', controller: 'contacts', constraints: EppConstraint.new(:contact) From 384d0cb96286db03f7ae22b6fe31583f4ca0a3e7 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 16:11:02 +0200 Subject: [PATCH 35/97] Improve changelog, certificate validation --- CHANGELOG.md | 37 ++++++++++++++++++++-- app/controllers/epp/sessions_controller.rb | 12 ++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f0cc390..a7ac4eee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Go to registry shared folder and setup CA directory tree: ``` mkdir ca cd ca -mkdir certs crl newcerts private +mkdir certs crl newcerts private csrs chmod 700 private touch index.txt echo 1000 > serial @@ -45,12 +45,13 @@ authorityKeyIdentifier=keyid:always,issuer basicConstraints = CA:true keyUsage = cRLSign, keyCertSign +# For the CA policy [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional -commonName = optional +commonName = supplied emailAddress = optional ``` @@ -60,6 +61,18 @@ openssl req -new -x509 -days 3650 -key private/ca.key.pem -sha256 -extensions v3 chmod 444 certs/ca.cert.pem ``` +Create a CSR for the webclient: +``` +openssl genrsa -out private/webclient.key.pem 4096 +chmod 400 private/webclient.key.pem +openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem +``` + +Sign the request and create certificate: +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.cert.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.cert.pem +``` + Configure EPP virtual host: ``` sudo nano /etc/apache2/sites-enabled/epp.conf @@ -75,10 +88,30 @@ With these lines: SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" +``` + +Configure webclient virtual host: +``` + SSLVerifyClient none + SSLVerifyDepth 1 + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem + + RequestHeader set SSL_CLIENT_S_DN_CN "" + + + SSLVerifyClient require + + + + SSLVerifyClient require + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" + ``` Reload apache: ``` +sudo a2enmod headers sudo /etc/init.d/apache2 reload ``` diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 4d7e2481b..53ac68421 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -3,15 +3,23 @@ class Epp::SessionsController < EppController render_epp_response('greeting') end + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity def login + cert_valid = true # Allow login with only username if request.ip == APP_CONFIG['webclient_ip'] && login_params[:password].nil? @api_user = ApiUser.find_by(username: login_params[:username]) + elsif request.ip == APP_CONFIG['webclient_ip'] + @api_user = ApiUser.find_by(login_params) else + if request.env['HTTP_SSL_CLIENT_S_DN_CN'] != login_params[:username] + cert_valid = false + end @api_user = ApiUser.find_by(login_params) end - if @api_user.try(:active) + if @api_user.try(:active) && cert_valid epp_session[:api_user_id] = @api_user.id render_epp_response('login_success') else @@ -19,6 +27,8 @@ class Epp::SessionsController < EppController render_epp_response('login_fail') end end + # rubocop: enable Metrics/PerceivedComplexity + # rubocop: enable Metrics/CyclomaticComplexity def logout @api_user = current_user # cache current_user for logging From 0f3c8ba94417b49ac34ffc42ddb7559e6e5ecf6e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 16:21:25 +0200 Subject: [PATCH 36/97] Improve changelog --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 679be1f81..1fafac27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,11 @@ With these lines: ``` Configure webclient virtual host: +``` +sudo nano /etc/apache2/sites-enabled/webclient.conf +``` + +Add these lines: ``` SSLVerifyClient none SSLVerifyDepth 1 @@ -115,13 +120,19 @@ sudo a2enmod headers sudo /etc/init.d/apache2 reload ``` -Configure application.yml to match the CA settings: +Configure registry 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' ``` +Configure webclient application.yml to match the CA settings: +``` +cert_path: '/home/registry/registry/shared/ca/certs/webclient.cert.pem' +key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' +``` + 20.01.2015 * Added dedicated mina cron:setup and mina cron:clear for manual cron management. From 4f290bc9fe638f8a0041ed94f529e212d1d63ef9 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 17:48:30 +0200 Subject: [PATCH 37/97] Enable certificate creating on api users --- CHANGELOG.md | 5 +++-- app/models/api_user.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fafac27f..0b382086b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,14 +117,15 @@ Add these lines: Reload apache: ``` sudo a2enmod headers -sudo /etc/init.d/apache2 reload +sudo /etc/init.d/apache2 restart ``` -Configure registry application.yml to match the CA settings: +Configure registry and epp 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' +webclient_ip: '54.154.91.240' ``` Configure webclient application.yml to match the CA settings: diff --git a/app/models/api_user.rb b/app/models/api_user.rb index aee1ff203..1dd0b6ea6 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 From 4a5234f7892b4cd72f1d898becc6423eadb344ab Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 17:58:48 +0200 Subject: [PATCH 38/97] Show crt download link --- app/views/admin/api_users/show.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/admin/api_users/show.haml b/app/views/admin/api_users/show.haml index f8f09a701..63ffc3952 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 adfe2b3ddb58de26700c4a4024b67abf9f447e5e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 19 Feb 2015 18:21:59 +0200 Subject: [PATCH 39/97] Improve readme --- CHANGELOG.md | 15 +++++----- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b382086b..fa40a47bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,8 +57,8 @@ 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 +openssl req -new -x509 -days 3650 -key private/ca.key.pem -sha256 -extensions v3_ca -out certs/ca.crt.pem +chmod 444 certs/ca.crt.pem ``` Create a CSR for the webclient: @@ -70,7 +70,7 @@ openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr. Sign the request and create certificate: ``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.cert.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.cert.pem +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem ``` Configure EPP virtual host: @@ -87,7 +87,7 @@ With these lines: ``` SSLVerifyClient require SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` @@ -100,7 +100,7 @@ Add these lines: ``` SSLVerifyClient none SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem RequestHeader set SSL_CLIENT_S_DN_CN "" @@ -109,7 +109,6 @@ Add these lines: - SSLVerifyClient require RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` @@ -122,7 +121,7 @@ sudo /etc/init.d/apache2 restart Configure registry and epp application.yml to match the CA settings: ``` -ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.cert.pem' +ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' ca_key_password: 'registryalpha' webclient_ip: '54.154.91.240' @@ -130,7 +129,7 @@ webclient_ip: '54.154.91.240' Configure webclient application.yml to match the CA settings: ``` -cert_path: '/home/registry/registry/shared/ca/certs/webclient.cert.pem' +cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' ``` diff --git a/README.md b/README.md index 92b8f67c6..2733fd2bd 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,8 @@ Be sure to update paths to match your system configuration. SSLVerifyClient require SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.cert.pem + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" EPPEngine On EPPCommandRoot /proxy/command @@ -181,6 +182,86 @@ All registry demo data can be found at: Initially you can use two type of users: admin users and EPP users. +### CA + +Go to registry shared folder and setup CA directory tree: +``` +mkdir ca +cd ca +mkdir certs crl newcerts private csrs +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 + +# For the CA policy +[ policy_match ] +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +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.crt.pem +chmod 444 certs/ca.crt.pem +``` + +Create a CSR for the webclient: +``` +openssl genrsa -out private/webclient.key.pem 4096 +chmod 400 private/webclient.key.pem +openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem +``` + +Sign the request and create certificate: +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem +``` + +Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. Certificates are created automatically after saving the user. + +Private key and certificate must be packaged to pkcs12 and added to the browser's certificate bank. + +Make sure application configuration files contain correct paths to certificates. ### EPP web client From fe67c5143649b1cf77d276997b6303e105d91801 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Fri, 20 Feb 2015 11:58:49 +0200 Subject: [PATCH 40/97] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa40a47bc..cdee71470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,7 +108,8 @@ Add these lines: SSLVerifyClient require - + + SSLVerifyClient require RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` From fd500f4ee108c95ae552b2139a6e474d2ef46a30 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 17:43:20 +0200 Subject: [PATCH 41/97] Added alpha travis setup --- .travis.yml | 15 +++++++++++++++ Gemfile | 3 +++ Gemfile.lock | 1 + lib/tasks/test.rake | 2 +- 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..9169f9af9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: ruby +rvm: + - 2.2 + - ruby-head + +sudo: false +script: 'ci/travis.rb' +before_install: + - gem install bundler + - "rm ${BUNDLE_GEMFILE}.lock" +before_script: + - bundle update +cache: bundler +addons: + postgresql: "9.3" diff --git a/Gemfile b/Gemfile index c901c0e56..b9360ace8 100644 --- a/Gemfile +++ b/Gemfile @@ -110,4 +110,7 @@ group :development, :test do # dev tools gem 'unicorn' + + # for travis + gem 'rake' end diff --git a/Gemfile.lock b/Gemfile.lock index 9097efcef..eb77b8396 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -478,6 +478,7 @@ DEPENDENCIES railroady (~> 1.3.0) rails (= 4.2.0) rails-settings-cached (~> 0.4.1) + rake ransack (~> 1.5.1) rspec-rails (~> 3.0.2) rubocop (~> 0.26.1) diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake index 20e3f5f91..ac5b5159d 100644 --- a/lib/tasks/test.rake +++ b/lib/tasks/test.rake @@ -30,7 +30,7 @@ begin end Rake::Task[:default].prerequisites.clear - task default: :test + task default: 'test:other' def test_against_server _stdin, _stdout, _stderr, wait_thr = Open3.popen3('unicorn -E test -p 8989') From 76af6a8182469aa70009c6e16d46c99baff8d124 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 18:19:11 +0200 Subject: [PATCH 42/97] Added travis database yml --- .travis.yml | 9 +++++++-- config/database-travis.yml | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 config/database-travis.yml diff --git a/.travis.yml b/.travis.yml index 9169f9af9..22d8154dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,19 @@ language: ruby rvm: - 2.2 - ruby-head - +env: + - DB=postgresql sudo: false -script: 'ci/travis.rb' before_install: - gem install bundler - "rm ${BUNDLE_GEMFILE}.lock" before_script: - bundle update + - cp config/application-example.yml config/application.yml + - cp config/database-travis.yml config/database.yml + - RAILS_ENV=test bundle exec rake db:all:setup +script: + - RAILS_ENV=test bundle exec rake cache: bundler addons: postgresql: "9.3" diff --git a/config/database-travis.yml b/config/database-travis.yml new file mode 100644 index 000000000..bf0765265 --- /dev/null +++ b/config/database-travis.yml @@ -0,0 +1,19 @@ +default: &default + host: localhost + adapter: postgresql + encoding: unicode + pool: 5 + username: postgres + password: + +test: + <<: *default + database: registry_test + +whois_test: + <<: *default + database: registry_whois_test + +api_log_test: + <<: *default + database: registry_api_log_test From 9e92cced85608b0f085984cd9297d827a45d85fc Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 18:51:22 +0200 Subject: [PATCH 43/97] Updated travis postgres setup --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22d8154dc..9308b354d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,18 @@ before_install: - gem install bundler - "rm ${BUNDLE_GEMFILE}.lock" before_script: + - psql -c 'create database registry_test;' -U postgres + - psql -c 'create database registry_whois_test;' -U postgres + - psql -c 'create database registry_api_log_test;' -U postgres - bundle update - cp config/application-example.yml config/application.yml - cp config/database-travis.yml config/database.yml - - RAILS_ENV=test bundle exec rake db:all:setup + - RAILS_ENV=test bundle exec rake db:all:schema:load + - RAILS_ENV=test bundle exec rake db:seed script: - RAILS_ENV=test bundle exec rake cache: bundler +services: + - postgresql addons: postgresql: "9.3" From 09b2cfbc13572250e4fdb480eb57da6534c4ddfc Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 19:05:23 +0200 Subject: [PATCH 44/97] Removed deprication warning about establish connection --- lib/tasks/db.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 3108ad02f..73f217711 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -41,7 +41,7 @@ namespace :db do databases.each do |name| begin puts "\n---------------------------- #{name} ----------------------------------------\n" - ActiveRecord::Base.establish_connection(name) + ActiveRecord::Base.establish_connection(name.to_sym) if ActiveRecord::Base.connection.table_exists?('schema_migrations') puts 'Found tables, skip schema load!' else From 1febb242ef43d959f2875dcc3b4924bcee24c2a8 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 19:08:05 +0200 Subject: [PATCH 45/97] Added secrets to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9308b354d..5fc709227 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ before_script: - psql -c 'create database registry_api_log_test;' -U postgres - bundle update - cp config/application-example.yml config/application.yml + - cp config/secrets-example.yml config/secrets.yml - cp config/database-travis.yml config/database.yml - RAILS_ENV=test bundle exec rake db:all:schema:load - RAILS_ENV=test bundle exec rake db:seed From 15c296fd362e26bcccafbd346629271b2ff67e8c Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 20 Feb 2015 19:21:57 +0200 Subject: [PATCH 46/97] Added travis badge --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2733fd2bd..8b1dabe6d 100644 --- a/README.md +++ b/README.md @@ -359,3 +359,8 @@ This needs a static greeting file, so you will have to make /var/www writable. mkdir epp Copy the files from $mod_epp/examples/cgis to /usr/lib/cgi-bin/epp + +## Code Status + +Alpha release status, only model tests: +[![Build Status](https://travis-ci.org/domify/registry.svg?branch=master)](https://travis-ci.org/domify/registry) From 00ecdba6990b3987fd3528ad164f4ed0a664f47b Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Mon, 23 Feb 2015 11:53:23 +0200 Subject: [PATCH 47/97] Put back password for pki login --- CHANGELOG.md | 9 +++++++++ app/api/repp/api.rb | 14 ++------------ app/controllers/epp/sessions_controller.rb | 5 +---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdee71470..d19f858a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ mkdir certs crl newcerts private csrs chmod 700 private touch index.txt echo 1000 > serial +echo 1000 > crlnumber ``` Generate the root key (prompts for pass phrase): @@ -26,6 +27,8 @@ exit Make sure the following options are in place: ``` +crl_extensions = crl_ext + [ CA_default ] # Where everything is kept dir = /home/registry/registry/shared/ca @@ -71,6 +74,12 @@ openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr. Sign the request and create certificate: ``` openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem +chmod 444 certs/webclient.crt.pem +``` + +Create certificate revocation list (prompts for pass phrase): +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem ``` Configure EPP virtual host: diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb index ec0b3167f..04b805597 100644 --- a/app/api/repp/api.rb +++ b/app/api/repp/api.rb @@ -3,18 +3,8 @@ module Repp format :json prefix :repp - before do - auth_param = request.headers['Authorization'].split(' ', 2).second - username, password = ::Base64.decode64(auth_param || '').split(':', 2) - - # allow user lookup only by username if request came from webclient - if request.ip == APP_CONFIG['webclient_ip'] && password.blank? - login_params = { username: username } - else - login_params = { username: username, password: password } - end - - @current_user ||= ApiUser.find_by(login_params) + http_basic do |username, password| + @current_user ||= ApiUser.find_by(username: username, password: password) end helpers do diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 6a4696e49..f12b2e56d 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -9,10 +9,7 @@ class Epp::SessionsController < EppController # rubocop: disable Metrics/CyclomaticComplexity def login cert_valid = true - # Allow login with only username - if request.ip == APP_CONFIG['webclient_ip'] && login_params[:password].nil? - @api_user = ApiUser.find_by(username: login_params[:username]) - elsif request.ip == APP_CONFIG['webclient_ip'] + if request.ip == APP_CONFIG['webclient_ip'] @api_user = ApiUser.find_by(login_params) else if request.env['HTTP_SSL_CLIENT_S_DN_CN'] != login_params[:username] From 62d76cee00faa4558c23099fc32d9bcda7d6069f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 23 Feb 2015 16:51:49 +0200 Subject: [PATCH 48/97] Fix polymorphic issue with papertrail --- Gemfile | 5 ++++- Gemfile.lock | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index b9360ace8..68a2b9571 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,10 @@ gem 'hashie_rails', '~> 0.0.1' # model related gem 'pg', '~> 0.18.0' gem 'ransack', '~> 1.5.1' # for searching -gem 'paper_trail', '~> 4.0.0.beta2' # archiving +# with polymorphic fix +gem 'paper_trail', + github: 'airblade/paper_trail', + ref: 'a453811226ec4ea59753ba6b827e390ced2fc140' # '~> 4.0.0.beta2' # archiving gem 'rails-settings-cached', '~> 0.4.1' # for settings gem 'delayed_job_active_record', '~> 4.0.3' # delayed job diff --git a/Gemfile.lock b/Gemfile.lock index eb77b8396..b798b512b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,13 @@ +GIT + remote: git://github.com/airblade/paper_trail.git + revision: a453811226ec4ea59753ba6b827e390ced2fc140 + ref: a453811226ec4ea59753ba6b827e390ced2fc140 + specs: + paper_trail (4.0.0.beta3) + activerecord (>= 3.0, < 6.0) + activesupport (>= 3.0, < 6.0) + request_store (~> 1.1.0) + GIT remote: git://github.com/haml/html2haml.git revision: 6984f50bdbbd6291535027726a5697f28778ee8d @@ -235,9 +245,6 @@ GEM nprogress-rails (0.1.6.5) open4 (1.3.4) orm_adapter (0.5.0) - paper_trail (4.0.0.beta2) - activerecord (>= 3.0, < 6.0) - activesupport (>= 3.0, < 6.0) parser (2.2.0.2) ast (>= 1.1, < 3.0) pg (0.18.1) @@ -309,6 +316,7 @@ GEM ruby_parser (~> 3.3) sexp_processor ref (1.0.5) + request_store (1.1.0) responders (2.0.2) railties (>= 4.2.0.alpha, < 5) rspec (3.0.0) @@ -469,7 +477,7 @@ DEPENDENCIES newrelic_rpm (~> 3.9.9.275) nokogiri (~> 1.6.2.1) nprogress-rails (~> 0.1.6.5) - paper_trail (~> 4.0.0.beta2) + paper_trail! pg (~> 0.18.0) phantomjs (~> 1.9.7.1) phantomjs-binaries (~> 1.9.2.4) From 0e13a1026e46cd674ff9df92d419c321f24a321b Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 23 Feb 2015 18:18:25 +0200 Subject: [PATCH 49/97] Added rake db:all:drop rake tasks and check init_settings --- config/initializers/initial_settings.rb | 2 +- lib/tasks/db.rake | 59 ++++++++++++++++++++----- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/config/initializers/initial_settings.rb b/config/initializers/initial_settings.rb index 97743775f..84090c28d 100644 --- a/config/initializers/initial_settings.rb +++ b/config/initializers/initial_settings.rb @@ -1,4 +1,4 @@ -if ActiveRecord::Base.connection.table_exists? 'settings' # otherwise rake not working 100% +if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('settings') # otherwise rake not working 100% Setting.disclosure_name = true if Setting.disclosure_name.nil? Setting.disclosure_name = true if Setting.disclosure_name.nil? Setting.disclosure_org_name = true if Setting.disclosure_org_name.nil? diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 73f217711..9f7a0a839 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -1,16 +1,14 @@ namespace :db do - def databases - @db ||= ["api_log_#{Rails.env}", "whois_#{Rails.env}", "#{Rails.env}"] + def other_databases + @db ||= ["api_log_#{Rails.env}", "whois_#{Rails.env}"] end def schema_file(db) case db - when databases.first + when "api_log_#{Rails.env}" 'api_log_schema.rb' - when databases.second + when "whois_#{Rails.env}" 'whois_schema.rb' - when databases.third - 'schema.rb' end end @@ -24,11 +22,41 @@ namespace :db do desc 'Create all databases: registry, api_log and whois' task create: [:environment] do - databases.each do |name| + puts "\n---------------------------- Create main database ----------------------------------------\n" + Rake::Task['db:create'].invoke + + other_databases.each do |name| begin - conf = ActiveRecord::Base.configurations + puts "\n---------------------------- Create #{name} ----------------------------------------\n" ActiveRecord::Base.clear_all_connections! - ActiveRecord::Base.connection.create_database(conf[name]['database'], conf[name]) + conf = ActiveRecord::Base.configurations + ActiveRecord::Base.connection.create_database(conf[name]['database'].to_sym, conf[name]) + rescue => e + puts "\n#{e}" + end + end + end + + desc 'Drop all databaseses: registry, api_log and whois' + task drop: [:environment] do + # just in case we allow only drop test, comment it out please for temp + return unless Rails.env.test? + + puts "\n---------------------------- Drop main database ----------------------------------------\n" + Rake::Task['db:drop'].invoke + + other_databases.each do |name| + begin + puts "\n---------------------------- #{name} dropped ----------------------------------------\n" + ActiveRecord::Base.clear_all_connections! + ActiveRecord::Base.establish_connection(name.to_sym) + + conf = ActiveRecord::Base.configurations + if ActiveRecord::Tasks::DatabaseTasks.drop(conf[name]) + puts "#{conf[name]['database']} dropped" + else + puts "Didn't find database #{name}, no drop" + end rescue => e puts "\n#{e}" end @@ -38,9 +66,13 @@ namespace :db do namespace :schema do desc 'Schema load for all databases: registry, api_log and whois' task load: [:environment] do - databases.each do |name| + puts "\n---------------------------- Main schema load ----------------------------------------\n" + Rake::Task['db:schema:load'].invoke + + other_databases.each do |name| begin - puts "\n---------------------------- #{name} ----------------------------------------\n" + puts "\n---------------------------- #{name} schema loaded ----------------------------------------\n" + ActiveRecord::Base.clear_all_connections! ActiveRecord::Base.establish_connection(name.to_sym) if ActiveRecord::Base.connection.table_exists?('schema_migrations') puts 'Found tables, skip schema load!' @@ -55,7 +87,10 @@ namespace :db do desc 'Schema load for all databases: registry, api_log and whois' task dump: [:environment] do - databases.each do |name| + puts "\n---------------------------- Main schema load ----------------------------------------\n" + Rake::Task['db:schema:dump'].invoke + + other_databases.each do |name| begin puts "\n---------------------------- #{name} ----------------------------------------\n" filename = "#{Rails.root}/db/#{schema_file(name)}" From 5319db16b4558f7d0e3fef7b54586b79de6b85a1 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 25 Feb 2015 16:26:09 +0200 Subject: [PATCH 50/97] Add new resource for certs --- app/controllers/admin/api_users_controller.rb | 8 -- .../admin/certificates_controller.rb | 68 +++++++++++++++ app/models/ability.rb | 9 +- app/models/api_user.rb | 1 + app/models/certificate.rb | 83 +++++++++++++++++++ app/views/admin/api_users/show.haml | 37 +++++---- app/views/admin/certificates/new.haml | 20 +++++ app/views/admin/certificates/show.haml | 75 +++++++++++++++++ config/locales/en.yml | 7 ++ config/routes.rb | 10 ++- .../20150223104842_create_certificates.rb | 15 ++++ db/schema.rb | 10 ++- 12 files changed, 310 insertions(+), 33 deletions(-) create mode 100644 app/controllers/admin/certificates_controller.rb create mode 100644 app/models/certificate.rb create mode 100644 app/views/admin/certificates/new.haml create mode 100644 app/views/admin/certificates/show.haml create mode 100644 db/migrate/20150223104842_create_certificates.rb diff --git a/app/controllers/admin/api_users_controller.rb b/app/controllers/admin/api_users_controller.rb index 54105e87e..7336c1d1e 100644 --- a/app/controllers/admin/api_users_controller.rb +++ b/app/controllers/admin/api_users_controller.rb @@ -53,14 +53,6 @@ class Admin::ApiUsersController < AdminController end end - def download_csr - send_data @api_user.csr, filename: "#{@api_user.username}.csr.pem" - end - - def download_crt - send_data @api_user.crt, filename: "#{@api_user.username}.crt.pem" - end - private def set_api_user diff --git a/app/controllers/admin/certificates_controller.rb b/app/controllers/admin/certificates_controller.rb new file mode 100644 index 000000000..4bc05fec4 --- /dev/null +++ b/app/controllers/admin/certificates_controller.rb @@ -0,0 +1,68 @@ +class Admin::CertificatesController < AdminController + load_and_authorize_resource + before_action :set_certificate, :set_api_user, only: [:sign, :show, :download_csr, :download_crt, :revoke] + + def show + @csr = OpenSSL::X509::Request.new(@certificate.csr) if @certificate.csr + @crt = OpenSSL::X509::Certificate.new(@certificate.crt) if @certificate.crt + end + + def new + set_api_user + @certificate = Certificate.new + end + + def create + @api_user = ApiUser.find(params[:api_user_id]) + csr = certificate_params[:csr].open.read if certificate_params[:csr] + + @certificate = @api_user.certificates.build(csr: csr) + if @api_user.save + flash[:notice] = I18n.t('record_created') + redirect_to [:admin, @api_user, @certificate] + else + flash.now[:alert] = I18n.t('failed_to_create_record') + render 'new' + end + end + + def sign + if @certificate.sign! + flash[:notice] = I18n.t('record_updated') + else + flash[:alert] = I18n.t('failed_to_update_record') + end + redirect_to [:admin, @api_user, @certificate] + end + + def revoke + if @certificate.revoke! + flash[:notice] = I18n.t('record_updated') + else + flash[:alert] = I18n.t('failed_to_update_record') + end + redirect_to [:admin, @api_user, @certificate] + end + + def download_csr + send_data @certificate.csr, filename: "#{@api_user.username}.csr.pem" + end + + def download_crt + send_data @certificate.crt, filename: "#{@api_user.username}.crt.pem" + end + + private + + def set_certificate + @certificate = Certificate.find(params[:id]) + end + + def set_api_user + @api_user = ApiUser.find(params[:api_user_id]) + end + + def certificate_params + params.require(:certificate).permit(:csr) + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 666ea6685..bb47bb2a8 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -5,7 +5,7 @@ class Ability alias_action :show, :create, :update, :destroy, to: :crud @user = user || AdminUser.new - + case @user.class.to_s when 'AdminUser' @user.roles.each { |role| send(role) } if @user.roles @@ -18,11 +18,11 @@ class Ability def epp # Epp::Contact - can(:info, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } + 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(: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 @@ -45,6 +45,7 @@ class Ability can :manage, DomainVersion can :manage, User can :manage, ApiUser + can :manage, Certificate can :manage, Keyrelay can :manage, LegalDocument can :read, ApiLog::EppLog diff --git a/app/models/api_user.rb b/app/models/api_user.rb index 1dd0b6ea6..6d71b1671 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -5,6 +5,7 @@ class ApiUser < User # TODO: should have max request limit per day belongs_to :registrar has_many :contacts + has_many :certificates validates :username, :password, :registrar, presence: true validates :username, uniqueness: true diff --git a/app/models/certificate.rb b/app/models/certificate.rb new file mode 100644 index 000000000..ce46c2b6e --- /dev/null +++ b/app/models/certificate.rb @@ -0,0 +1,83 @@ +class Certificate < ActiveRecord::Base + SIGNED = 'signed' + UNSIGNED = 'unsigned' + EXPIRED = 'expired' + REVOKED = 'revoked' + VALID = 'valid' + + validates :csr, presence: true + + def parsed_crt + @p_crt ||= OpenSSL::X509::Certificate.new(crt) if crt + end + + def parsed_csr + @p_csr ||= OpenSSL::X509::Request.new(csr) if csr + end + + def revoked? + status == REVOKED + end + + def status + return UNSIGNED if crt.blank? + return @cached_status if @cached_status + + @cached_status = SIGNED + + if parsed_crt.not_before > Time.now.utc && parsed_crt.not_after < Time.now.utc + @cached_status = EXPIRED + end + + crl = OpenSSL::X509::CRL.new(File.open(APP_CONFIG['crl_path']).read) + return @cached_status unless crl.revoked.map(&:serial).include?(parsed_crt.serial) + + @cached_status = REVOKED + end + + def sign! + csr_file = Tempfile.new('client_csr') + csr_file.write(csr) + csr_file.rewind + + crt_file = Tempfile.new('client_crt') + _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ + -cert #{APP_CONFIG['ca_cert_path']} \ + -extensions usr_cert -notext -md sha256 \ + -in #{csr_file.path} -out #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") + + if err.match(/Data Base Updated/) + crt_file.rewind + self.crt = crt_file.read + save! + else + errors.add(:base, I18n.t('failed_to_create_certificate')) + logger.error('FAILED TO CREATE CLIENT CERTIFICATE') + logger.error(err) + return false + end + end + + def revoke! + crt_file = Tempfile.new('client_crt') + crt_file.write(crt) + crt_file.rewind + + _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ + -cert #{APP_CONFIG['ca_cert_path']} \ + -revoke #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") + + if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/) + save! + else + errors.add(:base, I18n.t('failed_to_revoke_certificate')) + logger.error('FAILED TO REVOKE CLIENT CERTIFICATE') + logger.error(err) + return false + end + + _out, _err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ + -cert #{APP_CONFIG['ca_cert_path']} \ + -gencrl -out #{APP_CONFIG['crl_path']} -key '#{APP_CONFIG['ca_key_password']}' -batch") + end +end diff --git a/app/views/admin/api_users/show.haml b/app/views/admin/api_users/show.haml index 63ffc3952..fc798370e 100644 --- a/app/views/admin/api_users/show.haml +++ b/app/views/admin/api_users/show.haml @@ -15,7 +15,7 @@ - if @api_user.errors.any? %hr .row - .col-md-6 + .col-md-12 .panel.panel-default .panel-heading %h3.panel-title= t('general') @@ -29,21 +29,24 @@ %dt= t('active') %dd= @api_user.active - - .col-md-6 +.row + .col-md-12 .panel.panel-default - .panel-heading - %h3.panel-title= t('certificates') - .panel-body - %dl.dl-horizontal - %dt= t('csr') - - if @api_user.csr - %dd= link_to(t('download'), download_csr_admin_api_user_path) - - else - %dd - + .panel-heading.clearfix + .pull-left + = t('certificates') + .pull-right + = link_to(t('upload_csr'), new_admin_api_user_certificate_path(@api_user), class: 'btn btn-primary btn-xs') - %dt= t('crt') - - if @api_user.csr - %dd= link_to(t('download'), download_crt_admin_api_user_path) - - else - %dd - + .table-responsive + %table.table.table-hover.table-bordered.table-condensed + %thead + %tr + %th{class: 'col-xs-10'}= t('subject') + %th{class: 'col-xs-2'}= t('status') + %tbody + - @api_user.certificates.each do |x| + - if x.csr + %tr + %td= link_to(x.parsed_csr.try(:subject), admin_api_user_certificate_path(@api_user, x)) + %td= x.status diff --git a/app/views/admin/certificates/new.haml b/app/views/admin/certificates/new.haml new file mode 100644 index 000000000..f0c1fe7ce --- /dev/null +++ b/app/views/admin/certificates/new.haml @@ -0,0 +1,20 @@ +%h2= t('upload_csr') +%hr += form_for([:admin, @api_user, @certificate], multipart: true) do |f| + - if @certificate.errors.any? + - @certificate.errors.each do |attr, err| + = err + %br + - if @certificate.errors.any? + %hr + + .row + .col-md-12.text-left + .form-group + = f.label :csr, t('certificate_signing_req') + = f.file_field :csr + %hr + .row + .col-md-12.text-right + = button_tag(t('save'), class: 'btn btn-primary') + diff --git a/app/views/admin/certificates/show.haml b/app/views/admin/certificates/show.haml new file mode 100644 index 000000000..b03bcdc4d --- /dev/null +++ b/app/views/admin/certificates/show.haml @@ -0,0 +1,75 @@ +.row + .col-sm-6 + %h2.text-center-xs + = t('certificates') + .col-sm-6 + %h2.text-right.text-center-xs + = link_to(t('back_to_api_user'), [:admin, @api_user], class: 'btn btn-default') + +%hr +- if @certificate.errors.any? + - @certificate.errors.each do |attr, err| + = err + %br +- if @certificate.errors.any? + %hr +.row + .col-md-12 + .panel.panel-default + .panel-heading.clearfix + .pull-left + = t('csr') + .pull-right + = link_to(t('download'), download_csr_admin_api_user_certificate_path(@api_user, @certificate), class: 'btn btn-default btn-xs') + - unless @crt + = link_to(t('sign_this_request'), sign_admin_api_user_certificate_path(@api_user, @certificate), method: :post, class: 'btn btn-primary btn-xs') + + .panel-body + %dl.dl-horizontal + %dt= t('version') + %dd= @csr.version + + %dt= t('subject') + %dd= @csr.subject + + %dt= t('signature_algorithm') + %dd= @csr.signature_algorithm + +- if @crt + .row + .col-md-12 + .panel.panel-default + .panel-heading.clearfix + .pull-left + = t('crt') unless @certificate.revoked? + = t('crt_revoked') if @certificate.revoked? + .pull-right + = link_to(t('download'), download_crt_admin_api_user_certificate_path(@api_user, @certificate), class: 'btn btn-default btn-xs') + - unless @certificate.revoked? + = link_to(t('revoke_this_certificate'), revoke_admin_api_user_certificate_path(@api_user, @certificate), method: :post, class: 'btn btn-primary btn-xs') + - if @crt + .panel-body + %dl.dl-horizontal + %dt= t('version') + %dd= @crt.version + + %dt= t('serial_number') + %dd= @crt.serial + + %dt= t('signature_algorithm') + %dd= @crt.signature_algorithm + + %dt= t('issuer') + %dd= @crt.issuer + + %dt= t('valid_from') + %dd= @crt.not_before + + %dt= t('valid_to') + %dd= @crt.not_after + + %dt= t('subject') + %dd= @crt.subject + + %dt= t('extensions') + %dd= @crt.extensions.map(&:to_s).join('
').html_safe diff --git a/config/locales/en.yml b/config/locales/en.yml index 3adea8723..49263383b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -481,4 +481,11 @@ en: address_help: 'Street name, house no - apartment no, city, county, country, zip' download: 'Download' failed_to_create_certificate: 'Failed to create certificate!' + failed_to_revoke_certificate: 'Failed to revoke certificate!' contact_code: Contact code + upload_csr: 'Upload CSR' + signature_algorithm: 'Signature algorithm' + version: 'Version' + sign_this_request: 'Sign this request' + revoke_this_certificate: 'Revoke this certificate' + crt_revoked: 'CRT (revoked)' diff --git a/config/routes.rb b/config/routes.rb index e2bb835b3..225ecaca2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,9 +48,13 @@ Rails.application.routes.draw do resources :admin_users resources :api_users do - member do - get 'download_csr' - get 'download_crt' + resources :certificates do + member do + post 'sign' + post 'revoke' + get 'download_csr' + get 'download_crt' + end end end diff --git a/db/migrate/20150223104842_create_certificates.rb b/db/migrate/20150223104842_create_certificates.rb new file mode 100644 index 000000000..6a1c55503 --- /dev/null +++ b/db/migrate/20150223104842_create_certificates.rb @@ -0,0 +1,15 @@ +class CreateCertificates < ActiveRecord::Migration + def change + create_table :certificates do |t| + t.integer :api_user_id + t.text :csr + t.text :crt + + t.timestamps + end + + ApiUser.all.each do |x| + x.certificates << Certificate.new(crt: x.crt, csr: x.csr) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f245f12ff..82b269b56 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: 20150217133937) do +ActiveRecord::Schema.define(version: 20150223104842) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -53,6 +53,14 @@ ActiveRecord::Schema.define(version: 20150217133937) do add_index "cached_nameservers", ["hostname", "ipv4", "ipv6"], name: "index_cached_nameservers_on_hostname_and_ipv4_and_ipv6", unique: true, using: :btree + create_table "certificates", force: :cascade do |t| + t.integer "api_user_id" + t.text "csr" + t.text "crt" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "contact_disclosures", force: :cascade do |t| t.integer "contact_id" t.boolean "phone" From a6baf60e59ba83aaeeff1c86e53769f61f0b5626 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 25 Feb 2015 17:33:32 +0200 Subject: [PATCH 51/97] Readme updates + tests for cert --- CHANGELOG.md | 5 ++ README.md | 19 +++++- app/models/certificate.rb | 5 ++ app/models/version/certificate_version.rb | 4 ++ config/application-example.yml | 1 + .../20150223104842_create_certificates.rb | 14 +++++ db/schema.rb | 14 +++++ spec/fabricators/certificate_fabricator.rb | 33 ++++++++++ spec/models/certificate_spec.rb | 61 +++++++++++++++++++ 9 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 app/models/version/certificate_version.rb create mode 100644 spec/fabricators/certificate_fabricator.rb create mode 100644 spec/models/certificate_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index d19f858a1..b322d303e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,8 @@ With these lines: SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` @@ -110,6 +112,8 @@ Add these lines: SSLVerifyClient none SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "" @@ -134,6 +138,7 @@ Configure registry and epp application.yml to match the CA settings: ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' ca_key_password: 'registryalpha' +crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' webclient_ip: '54.154.91.240' ``` diff --git a/README.md b/README.md index 8b1dabe6d..384814f8f 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,9 @@ Be sure to update paths to match your system configuration. SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCARevocationCheck chain + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" EPPEngine On @@ -192,6 +195,7 @@ mkdir certs crl newcerts private csrs chmod 700 private touch index.txt echo 1000 > serial +echo 1000 > crlnumber ``` Generate the root key (prompts for pass phrase): @@ -257,12 +261,23 @@ Sign the request and create certificate: openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem ``` -Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. Certificates are created automatically after saving the user. +Create certificate revocation list (prompts for pass phrase): +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem +``` -Private key and certificate must be packaged to pkcs12 and added to the browser's certificate bank. +Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. + +Private key and certificate must be packaged to pkcs12 and added to the browser. Make sure application configuration files contain correct paths to certificates. +In test environment it's important to set unique_subject option to false. +In CA directory: +``` +echo "unique_subject = no" > index.txt.attr +``` + ### EPP web client Please follow EPP web client readme: diff --git a/app/models/certificate.rb b/app/models/certificate.rb index ce46c2b6e..30cbbe949 100644 --- a/app/models/certificate.rb +++ b/app/models/certificate.rb @@ -1,4 +1,8 @@ class Certificate < ActiveRecord::Base + include Versions + + belongs_to :api_user + SIGNED = 'signed' UNSIGNED = 'unsigned' EXPIRED = 'expired' @@ -69,6 +73,7 @@ class Certificate < ActiveRecord::Base if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/) save! + @cached_status = REVOKED else errors.add(:base, I18n.t('failed_to_revoke_certificate')) logger.error('FAILED TO REVOKE CLIENT CERTIFICATE') diff --git a/app/models/version/certificate_version.rb b/app/models/version/certificate_version.rb new file mode 100644 index 000000000..bcc89e62a --- /dev/null +++ b/app/models/version/certificate_version.rb @@ -0,0 +1,4 @@ +class CertificateVersion < PaperTrail::Version + self.table_name = :log_certificates + self.sequence_name = :log_certificates_id_seq +end diff --git a/config/application-example.yml b/config/application-example.yml index 51a4661e5..4ac8d0ddb 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -10,6 +10,7 @@ defaults: &defaults ca_cert_path: ca-cert-path-here ca_key_path: ca-key-path-here ca_key_password: ca-key-pass-phrase-here + crl_path: crl-path-here development: <<: *defaults diff --git a/db/migrate/20150223104842_create_certificates.rb b/db/migrate/20150223104842_create_certificates.rb index 6a1c55503..b84e9b5ce 100644 --- a/db/migrate/20150223104842_create_certificates.rb +++ b/db/migrate/20150223104842_create_certificates.rb @@ -4,10 +4,24 @@ class CreateCertificates < ActiveRecord::Migration t.integer :api_user_id t.text :csr t.text :crt + t.string :creator_str + t.string :updator_str t.timestamps end + create_table :log_certificates do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.json "object" + t.json "object_changes" + t.datetime "created_at" + t.string "session" + t.json "children" + end + ApiUser.all.each do |x| x.certificates << Certificate.new(crt: x.crt, csr: x.csr) end diff --git a/db/schema.rb b/db/schema.rb index 82b269b56..cb37d9166 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -57,6 +57,8 @@ ActiveRecord::Schema.define(version: 20150223104842) do t.integer "api_user_id" t.text "csr" t.text "crt" + t.string "creator_str" + t.string "updator_str" t.datetime "created_at" t.datetime "updated_at" end @@ -283,6 +285,18 @@ ActiveRecord::Schema.define(version: 20150223104842) do add_index "log_api_users", ["item_type", "item_id"], name: "index_log_api_users_on_item_type_and_item_id", using: :btree add_index "log_api_users", ["whodunnit"], name: "index_log_api_users_on_whodunnit", using: :btree + create_table "log_certificates", force: :cascade do |t| + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false + t.string "whodunnit" + t.json "object" + t.json "object_changes" + t.datetime "created_at" + t.string "session" + t.json "children" + end + create_table "log_contact_disclosures", force: :cascade do |t| t.string "item_type", null: false t.integer "item_id", null: false diff --git a/spec/fabricators/certificate_fabricator.rb b/spec/fabricators/certificate_fabricator.rb new file mode 100644 index 000000000..8e97b9ec5 --- /dev/null +++ b/spec/fabricators/certificate_fabricator.rb @@ -0,0 +1,33 @@ +# default fabricator should be reusable +Fabricator(:certificate) do + api_user + csr "-----BEGIN CERTIFICATE REQUEST-----\n" \ + "MIIE+DCCAuACAQAwgZ0xCzAJBgNVBAYTAkVFMREwDwYDVQQIDAhIYXJqdW1hYTEQ\n" \ + "MA4GA1UEBwwHVGFsbGlubjEbMBkGA1UECgwSRWVzdGkgSW50ZXJuZXRpIFNBMRIw\n" \ + "EAYDVQQLDAlSRUdJU1RSQVIxEjAQBgNVBAMMCXdlYmNsaWVudDEkMCIGCSqGSIb3\n" \ + "DQEJARYVd2ViY2xpZW50QGludGVybmV0LmVlMIICIjANBgkqhkiG9w0BAQEFAAOC\n" \ + "Ag8AMIICCgKCAgEAuXronFj8CxPWGkyUhXf+/WirkFGb8a/My2+7GvQWYE10Nq4C\n" \ + "u9wDgjU3AuLw8qzwEeE3Z5uxHXWfwnshXOF6aJNCQWUsrs0odCxw69iIwCNGKhyF\n" \ + "jljtx8uSH8RRSRc8BFIUkvUpmp8m7kZTlB4FDey+XaGy4p/rImiAiwfFMIJMjdE9\n" \ + "9gk0EGDbomgP6KC3Ss/iQfuOFCQWSqjFuvp3mygr193YplaPgeLM1ERIW1LVFGDK\n" \ + "jy6keZ3E/Vb4O4qUPDRgTMr2KWM3Auzh2hXCymHNWn3yRn5Q4KSjJbG/P7Kz5nfZ\n" \ + "kY3eVRBIBll+1Q0VV7g+1B48zzjZX2qiY3iL77MV1oL17KeOO3PAxsEtptdqNgUa\n" \ + "Fpp73dwPST1ZKvq8FSgDKcdTCziSeViGhXjJRpEMr8FoeKNO7nvd1maKN9HAOy75\n" \ + "eSxatj6LoQ+JFN7Ci3IbwKFI7BnIHbEr9eP7O7Qbhljz2GE9+GWUqr3zwUEgpFSI\n" \ + "crAnRHQI2ALakEMsryF416zg5yr/bJp8/IzgZLaKpBVLOL88sI6r+JRdM6QXvKYx\n" \ + "XhamV6bH6CrR8ZYN4okaZH6sAcy8eyBnEmc05h/KsDzTNadwadeZe73F+PltoEXH\n" \ + "XgtpTpQ8XarN1uLq99WD6gWilAx3LF/xetCO86+w/MkYBmfOrXge+WLUUW8CAwEA\n" \ + "AaAVMBMGCSqGSIb3DQEJBzEGDAR0ZXN0MA0GCSqGSIb3DQEBCwUAA4ICAQAkTlU3\n" \ + "RcI6UMRA7As2FJSph3QurPebQFoZhnhMD+hb6+hXip8MY77YxLwo/ihB9wghaZKL\n" \ + "uV0BxjdZgjDt9GhA8dtPgaCp5LvB6kQYvcEzRvitN2CpJhtz39rlF3gxuy+RtpNf\n" \ + "5KbC691FivoXur1qx9I7mc4snB3DTzLiJPIZ6nQzPYcSVpPCbns30N/i/sOdHO0o\n" \ + "9hP5wlhCdYrOxad993m+InpMDyDWhB1+TA9ZO7gYpg8S4kBX3Cz9OXe80Pe56ZdK\n" \ + "pcgjTXnUDjNSRRGamJib2lyZ/axMbb/etwyy3X+jBDuOQropkmgrPEFJHpgNlFah\n" \ + "BuW7KEASqbw5YxpTSc0nDk5uxBw3voL8fk9M1sX64tbzGAEBRZnrWGeb1mOLM/YI\n" \ + "K6ocAYSBhNmWUzpHTwL7qSeP9ztQUGzoGHyRjBdan+1U2G75Kpj+TjEm/X8wmtnq\n" \ + "3/qVhUYNEavcZbgR1gSE45+mS8NsD7Oq0Xdc0UKsVDbUcCGIkGG9+ERAbRznfi3W\n" \ + "qhChtUxySX8T3SmX5mviwlJ5OwQVjdUF1/2voPK0oFK7zV+wZqcuORzDKdqB8XV7\n" \ + "MDcQjza4EOB78OmcHDgQ7nMXuY7/UL4F+bRZosxPy43X2JId5d+/GpgV8sP9dzK8\n" \ + "UGJDNEZ2YsBbPuKZS+2eNZ8g3sjjFBeadvrQ1w==\n" \ + "-----END CERTIFICATE REQUEST-----" +end diff --git a/spec/models/certificate_spec.rb b/spec/models/certificate_spec.rb new file mode 100644 index 000000000..109b0a482 --- /dev/null +++ b/spec/models/certificate_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +describe Certificate do + it { should belong_to(:api_user) } + + context 'with invalid attribute' do + before :all do + @certificate = Certificate.new + end + + it 'should not be valid' do + @certificate.valid? + @certificate.errors.full_messages.should match_array([ + "Csr is missing" + ]) + end + + it 'should not have any versions' do + @certificate.versions.should == [] + end + end + + context 'with valid attributes' do + before :all do + @certificate = Fabricate(:certificate) + end + + it 'should be valid' do + @certificate.valid? + @certificate.errors.full_messages.should match_array([]) + end + + it 'should be valid twice' do + @certificate = Fabricate(:certificate) + @certificate.valid? + @certificate.errors.full_messages.should match_array([]) + end + + it 'should sign csr' do + @certificate.status.should == 'unsigned' + @certificate.sign! + @certificate.status.should == 'signed' + @certificate.crt.should_not be_blank + end + + it 'should revoke crt' do + @certificate.revoke! + @certificate.status.should == 'revoked' + end + + it 'should have one version' do + with_versioning do + @certificate.versions.should == [] + @certificate.csr = 'new_request' + @certificate.save + @certificate.errors.full_messages.should match_array([]) + @certificate.versions.size.should == 1 + end + end + end +end From 9ebbe91663c1766d37d082c451f9329af3776ceb Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 25 Feb 2015 18:44:48 +0200 Subject: [PATCH 52/97] Added country code to contact info request --- Guardfile | 10 +++++----- app/views/epp/contacts/info.xml.builder | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Guardfile b/Guardfile index e6d0a443a..358d58e80 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/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index ffe884485..24480062d 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -18,7 +18,7 @@ xml.epp_head do xml.tag!('contact:upID', @contact.updator.try(:registrar)) xml.tag!('contact:upDate', @contact.updated_at) end - xml.tag!('contact:ident', @contact.ident, type: @contact.ident_type) + xml.tag!('contact:ident', @contact.ident, type: @contact.ident_type, cc: @contact.ident_country_code) # xml.tag!('contact:trDate', '123') if false if can? :view_password, @contact xml.tag!('contact:authInfo') do From 7347fb7517fcd8e35fe7af77ef5a05044dcd3067 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 25 Feb 2015 19:00:14 +0200 Subject: [PATCH 53/97] Readme update --- CHANGELOG.md | 6 ++++-- README.md | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b322d303e..dbf25bd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,7 +98,8 @@ With these lines: SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - SSLCARevocationCheck chain + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" ``` @@ -113,7 +114,8 @@ Add these lines: SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - SSLCARevocationCheck chain + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "" diff --git a/README.md b/README.md index 384814f8f..bc2b9af13 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,8 @@ Be sure to update paths to match your system configuration. SSLVerifyDepth 1 SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - SSLCARevocationCheck chain + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" From 1b60853bc8f09a05024ae4113b388c2aa8d6c7c2 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 26 Feb 2015 10:35:45 +0200 Subject: [PATCH 54/97] Do not test certificate signing with default rake --- spec/models/certificate_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/certificate_spec.rb b/spec/models/certificate_spec.rb index 109b0a482..26656e51b 100644 --- a/spec/models/certificate_spec.rb +++ b/spec/models/certificate_spec.rb @@ -36,14 +36,14 @@ describe Certificate do @certificate.errors.full_messages.should match_array([]) end - it 'should sign csr' do + it 'should sign csr', epp: true do @certificate.status.should == 'unsigned' @certificate.sign! @certificate.status.should == 'signed' @certificate.crt.should_not be_blank end - it 'should revoke crt' do + it 'should revoke crt', epp: true do @certificate.revoke! @certificate.status.should == 'revoked' end From c3560fd19fd8f96ac040667b681aa8c920f480c3 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 26 Feb 2015 11:04:20 +0200 Subject: [PATCH 55/97] Get rid of old code --- app/controllers/admin/api_users_controller.rb | 12 +++------ app/models/api_user.rb | 25 ------------------- app/views/admin/api_users/_form.haml | 7 +----- 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/app/controllers/admin/api_users_controller.rb b/app/controllers/admin/api_users_controller.rb index 7336c1d1e..27bed6a80 100644 --- a/app/controllers/admin/api_users_controller.rb +++ b/app/controllers/admin/api_users_controller.rb @@ -12,10 +12,7 @@ class Admin::ApiUsersController < AdminController end def create - app = api_user_params - app[:csr] = params[:api_user][:csr].open.read if params[:api_user][:csr] - - @api_user = ApiUser.new(app) + @api_user = ApiUser.new(api_user_params) if @api_user.save flash[:notice] = I18n.t('record_created') @@ -31,10 +28,7 @@ class Admin::ApiUsersController < AdminController def edit; end def update - app = api_user_params - app[:csr] = params[:api_user][:csr].open.read if params[:api_user][:csr] - - if @api_user.update(app) + if @api_user.update(api_user_params) flash[:notice] = I18n.t('record_updated') redirect_to [:admin, @api_user] else @@ -60,6 +54,6 @@ class Admin::ApiUsersController < AdminController end def api_user_params - params.require(:api_user).permit(:username, :password, :csr, :active, :registrar_id, :registrar_typeahead) + params.require(:api_user).permit(:username, :password, :active, :registrar_id, :registrar_typeahead) end end diff --git a/app/models/api_user.rb b/app/models/api_user.rb index 6d71b1671..09a849277 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -10,8 +10,6 @@ class ApiUser < User validates :username, :password, :registrar, presence: true validates :username, uniqueness: true - before_save :create_crt, if: -> (au) { au.csr_changed? } - attr_accessor :registrar_typeahead def ability @@ -30,28 +28,5 @@ class ApiUser < User def queued_messages registrar.messages.queued end - - def create_crt - csr_file = Tempfile.new('client_csr') - csr_file.write(csr) - csr_file.rewind - - crt_file = Tempfile.new('client_crt') - _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ - -cert #{APP_CONFIG['ca_cert_path']} \ - -extensions usr_cert -notext -md sha256 \ - -in #{csr_file.path} -out #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") - - if err.match(/Data Base Updated/) - crt_file.rewind - self.crt = crt_file.read - return true - else - errors.add(:base, I18n.t('failed_to_create_certificate')) - logger.error('FAILED TO CREATE CLIENT CERTIFICATE') - logger.error(err) - return false - end - end end # rubocop: enable Metrics/ClassLength diff --git a/app/views/admin/api_users/_form.haml b/app/views/admin/api_users/_form.haml index b8fd1071f..29f8b1179 100644 --- a/app/views/admin/api_users/_form.haml +++ b/app/views/admin/api_users/_form.haml @@ -7,7 +7,7 @@ %hr .row - .col-md-6 + .col-md-12 .form-group = f.label :username = f.text_field(:username, class: 'form-control') @@ -26,11 +26,6 @@ %label{for: 'api_user_active'} = f.check_box(:active) = t('active') - - .col-md-6.text-left - .form-group - = f.label :csr, t('certificate_signing_req') - = f.file_field :csr %hr .row .col-md-12.text-right From 20fbe5eef756020db7557f7b7fdc27fb4e694988 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 11:09:22 +0200 Subject: [PATCH 56/97] Api user active by default --- app/models/api_user.rb | 6 ++++++ spec/models/api_user_spec.rb | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/app/models/api_user.rb b/app/models/api_user.rb index 09a849277..cd07282b7 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -17,6 +17,12 @@ class ApiUser < User end delegate :can?, :cannot?, to: :ability + after_initialize :set_defaults + def set_defaults + return unless new_record? + self.active = true + end + def registrar_typeahead @registrar_typeahead || registrar || nil end diff --git a/spec/models/api_user_spec.rb b/spec/models/api_user_spec.rb index a6c6fb102..9fb0d2fdc 100644 --- a/spec/models/api_user_spec.rb +++ b/spec/models/api_user_spec.rb @@ -20,6 +20,11 @@ describe ApiUser do it 'should not have any versions' do @api_user.versions.should == [] end + + it 'should be active by default' do + @api_user.active.should == true + end + end context 'with valid attributes' do From 30b81cf9c1554d5b21556c2122ae189f1d9e85fe Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 11:21:51 +0200 Subject: [PATCH 57/97] Refactored ca into separate doc --- CHANGELOG.md | 150 +------------------------------------------- doc/certificate.md | 153 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 149 deletions(-) create mode 100644 doc/certificate.md diff --git a/CHANGELOG.md b/CHANGELOG.md index dbf25bd66..e9fd7e7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,154 +1,6 @@ 19.02.2015 - -Go to registry shared folder and setup CA directory tree: -``` -mkdir ca -cd ca -mkdir certs crl newcerts private csrs -chmod 700 private -touch index.txt -echo 1000 > serial -echo 1000 > crlnumber -``` -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: -``` -crl_extensions = crl_ext - -[ 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 - -# For the CA policy -[ policy_match ] -countryName = optional -stateOrProvinceName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -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.crt.pem -chmod 444 certs/ca.crt.pem -``` - -Create a CSR for the webclient: -``` -openssl genrsa -out private/webclient.key.pem 4096 -chmod 400 private/webclient.key.pem -openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem -``` - -Sign the request and create certificate: -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem -chmod 444 certs/webclient.crt.pem -``` - -Create certificate revocation list (prompts for pass phrase): -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.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.crt.pem - SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - # Uncomment this when upgrading to apache 2.4: - # SSLCARevocationCheck chain - RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" -``` - -Configure webclient virtual host: -``` -sudo nano /etc/apache2/sites-enabled/webclient.conf -``` - -Add these lines: -``` - SSLVerifyClient none - SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem - SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - # Uncomment this when upgrading to apache 2.4: - # SSLCARevocationCheck chain - - RequestHeader set SSL_CLIENT_S_DN_CN "" - - - SSLVerifyClient require - - - - SSLVerifyClient require - RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" - -``` - -Reload apache: -``` -sudo a2enmod headers -sudo /etc/init.d/apache2 restart -``` - -Configure registry and epp application.yml to match the CA settings: -``` -ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' -ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' -ca_key_password: 'registryalpha' -crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' -webclient_ip: '54.154.91.240' -``` - -Configure webclient application.yml to match the CA settings: -``` -cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' -key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' -``` +* Cetrificate only enabled, please setup certificates following doc/certificate.md document. 20.01.2015 diff --git a/doc/certificate.md b/doc/certificate.md new file mode 100644 index 000000000..102d49e96 --- /dev/null +++ b/doc/certificate.md @@ -0,0 +1,153 @@ +Setting up certificates +----------------------- + +Go to registry shared folder and setup CA directory tree: +``` +mkdir ca +cd ca +mkdir certs crl newcerts private csrs +chmod 700 private +touch index.txt +echo 1000 > serial +echo 1000 > crlnumber +``` + +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: +``` +crl_extensions = crl_ext + +[ 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 + +# For the CA policy +[ policy_match ] +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +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.crt.pem +chmod 444 certs/ca.crt.pem +``` + +Create a CSR for the webclient: +``` +openssl genrsa -out private/webclient.key.pem 4096 +chmod 400 private/webclient.key.pem +openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem +``` + +Sign the request and create certificate: +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem +chmod 444 certs/webclient.crt.pem +``` + +Create certificate revocation list (prompts for pass phrase): +``` +openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.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.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" +``` + +Configure webclient virtual host: +``` +sudo nano /etc/apache2/sites-enabled/webclient.conf +``` + +Add these lines: +``` + SSLVerifyClient none + SSLVerifyDepth 1 + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain + + RequestHeader set SSL_CLIENT_S_DN_CN "" + + + SSLVerifyClient require + + + + SSLVerifyClient require + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" + +``` + +Reload apache: +``` +sudo a2enmod headers +sudo /etc/init.d/apache2 restart +``` + +Configure registry and epp application.yml to match the CA settings: +``` +ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' +ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' +ca_key_password: 'registryalpha' +crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' +webclient_ip: '54.154.91.240' +``` + +Configure webclient application.yml to match the CA settings: +``` +cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' +key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' +``` + From b60da692c3bf337991e8996f6d85ccd03a5cfd8e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 26 Feb 2015 11:24:57 +0200 Subject: [PATCH 58/97] Update doc --- doc/certificate.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/certificate.md b/doc/certificate.md index 102d49e96..d0a2f78cd 100644 --- a/doc/certificate.md +++ b/doc/certificate.md @@ -120,10 +120,6 @@ Add these lines: RequestHeader set SSL_CLIENT_S_DN_CN "" - - SSLVerifyClient require - - SSLVerifyClient require RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" From 4e5d7aea4916c9be63fb888a2a4d4dcf27847715 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 11:45:14 +0200 Subject: [PATCH 59/97] Ignore ca dir --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7f705cb07..b7ebd630e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ config/application.yml config/secrets.yml config/database.yml /export +/ca ## Environment normalisation: /.bundle From f11af415d9f55848a252d8717061b1ce31b5e899 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 11:48:38 +0200 Subject: [PATCH 60/97] Merged certificate doc --- README.md | 92 +------------------ doc/certificate.md | 224 +++++++++++++++++++++++---------------------- 2 files changed, 115 insertions(+), 201 deletions(-) diff --git a/README.md b/README.md index bc2b9af13..24740e064 100644 --- a/README.md +++ b/README.md @@ -186,98 +186,10 @@ All registry demo data can be found at: Initially you can use two type of users: admin users and EPP users. -### CA +### Certificates setup -Go to registry shared folder and setup CA directory tree: -``` -mkdir ca -cd ca -mkdir certs crl newcerts private csrs -chmod 700 private -touch index.txt -echo 1000 > serial -echo 1000 > crlnumber -``` +* [Certificates setup](/doc/certificates.md) -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 - -# For the CA policy -[ policy_match ] -countryName = optional -stateOrProvinceName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -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.crt.pem -chmod 444 certs/ca.crt.pem -``` - -Create a CSR for the webclient: -``` -openssl genrsa -out private/webclient.key.pem 4096 -chmod 400 private/webclient.key.pem -openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem -``` - -Sign the request and create certificate: -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem -``` - -Create certificate revocation list (prompts for pass phrase): -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem -``` - -Certificates for API Users are generated via the user interface. CSR must be uploaded for each API User. - -Private key and certificate must be packaged to pkcs12 and added to the browser. - -Make sure application configuration files contain correct paths to certificates. - -In test environment it's important to set unique_subject option to false. -In CA directory: -``` -echo "unique_subject = no" > index.txt.attr -``` ### EPP web client diff --git a/doc/certificate.md b/doc/certificate.md index d0a2f78cd..c1131ed61 100644 --- a/doc/certificate.md +++ b/doc/certificate.md @@ -1,149 +1,151 @@ -Setting up certificates ------------------------ +Certificates setup +------------------ -Go to registry shared folder and setup CA directory tree: -``` -mkdir ca -cd ca -mkdir certs crl newcerts private csrs -chmod 700 private -touch index.txt -echo 1000 > serial -echo 1000 > crlnumber -``` +Certificates for API Users are generated via registnry admin user interface. +CSR must be uploaded for each API User. + +Private key and certificate must be packaged to pkcs12 and added to user browser. + + +### Registry setup + +Setup CA directory tree: + + cd /home/registry/registry/shared + mkdir ca + cd ca + mkdir certs crl newcerts private csrs + chmod 700 private + touch index.txt + echo 1000 > serial + echo 1000 > crlnumber Generate the root key (prompts for pass phrase): -``` -openssl genrsa -aes256 -out private/ca.key.pem 4096 -``` + + 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 -``` + + sudo su - + cd /etc/ssl/ + cp openssl.cnf openssl.cnf.bak + nano openssl.cnf + exit Make sure the following options are in place: -``` -crl_extensions = crl_ext -[ CA_default ] -# Where everything is kept -dir = /home/registry/registry/shared/ca + crl_extensions = crl_ext -[ 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 + [ CA_default ] + # Where everything is kept + dir = /home/registry/registry/shared/ca -[ v3_ca ] -# Extensions for a typical CA -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:always,issuer -basicConstraints = CA:true -keyUsage = cRLSign, keyCertSign + [ 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 -# For the CA policy -[ policy_match ] -countryName = optional -stateOrProvinceName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional -``` + [ v3_ca ] + # Extensions for a typical CA + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer + basicConstraints = CA:true + keyUsage = cRLSign, keyCertSign + + # For the CA policy + [ policy_match ] + countryName = optional + stateOrProvinceName = optional + organizationName = optional + organizationalUnitName = optional + commonName = supplied + 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.crt.pem -chmod 444 certs/ca.crt.pem -``` + + openssl req -new -x509 -days 3650 -key private/ca.key.pem -sha256 -extensions v3_ca -out certs/ca.crt.pem + chmod 444 certs/ca.crt.pem Create a CSR for the webclient: -``` -openssl genrsa -out private/webclient.key.pem 4096 -chmod 400 private/webclient.key.pem -openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem -``` + + openssl genrsa -out private/webclient.key.pem 4096 + chmod 400 private/webclient.key.pem + openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem Sign the request and create certificate: -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem -chmod 444 certs/webclient.crt.pem -``` + + openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem + chmod 444 certs/webclient.crt.pem Create certificate revocation list (prompts for pass phrase): -``` -openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem -``` + + openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem Configure EPP virtual host: -``` -sudo nano /etc/apache2/sites-enabled/epp.conf -``` + + sudo nano /etc/apache2/sites-enabled/epp.conf Replace this line: -``` -SSLVerifyClient optional_no_ca -``` + + SSLVerifyClient optional_no_ca With these lines: -``` - SSLVerifyClient require - SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem - SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - # Uncomment this when upgrading to apache 2.4: - # SSLCARevocationCheck chain - RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" -``` + + SSLVerifyClient require + SSLVerifyDepth 1 + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" Configure webclient virtual host: -``` -sudo nano /etc/apache2/sites-enabled/webclient.conf -``` + + sudo nano /etc/apache2/sites-enabled/webclient.conf Add these lines: -``` - SSLVerifyClient none - SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem - SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem - # Uncomment this when upgrading to apache 2.4: - # SSLCARevocationCheck chain - RequestHeader set SSL_CLIENT_S_DN_CN "" + SSLVerifyClient none + SSLVerifyDepth 1 + SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + # Uncomment this when upgrading to apache 2.4: + # SSLCARevocationCheck chain - - SSLVerifyClient require - RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" - -``` + RequestHeader set SSL_CLIENT_S_DN_CN "" + + + SSLVerifyClient require + RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" + Reload apache: -``` -sudo a2enmod headers -sudo /etc/init.d/apache2 restart -``` + + sudo a2enmod headers + sudo /etc/init.d/apache2 restart Configure registry and epp application.yml to match the CA settings: -``` -ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' -ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' -ca_key_password: 'registryalpha' -crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' -webclient_ip: '54.154.91.240' -``` + + ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' + ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' + ca_key_password: 'registryalpha' + crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' + webclient_ip: '54.154.91.240' Configure webclient application.yml to match the CA settings: -``` -cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' -key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' -``` + cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' + key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' + +Development env +--------------- + +In development environment it's convenient to set unique_subject option to false, +thus you can generate quickly as many certs as you wish. + +In CA directory: + + echo "unique_subject = no" > index.txt.attr From df57204922df91569b4d064cc0c7abedc5ede194 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 13:34:55 +0200 Subject: [PATCH 61/97] CA readme update --- config/application-example.yml | 13 ++- doc/certificate.md | 156 +++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 71 deletions(-) diff --git a/config/application-example.yml b/config/application-example.yml index 4ac8d0ddb..f5b0bf2ac 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -7,10 +7,15 @@ defaults: &defaults # If you change this key, all old signed cookies will become invalid! secret_key_base: please-change-it-you-can-generate-it-with-rake-secret devise_secret: please-change-it-you-can-generate-it-with-rake-secret - ca_cert_path: ca-cert-path-here - ca_key_path: ca-key-path-here - ca_key_password: ca-key-pass-phrase-here - crl_path: crl-path-here + + # Used by registry admin server: + crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' + ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' + ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' + ca_key_password: 'your-root-key-password' + + # Used by EPP server + webclient_ip: '54.154.91.240' development: <<: *defaults diff --git a/doc/certificate.md b/doc/certificate.md index c1131ed61..bf222a5e2 100644 --- a/doc/certificate.md +++ b/doc/certificate.md @@ -1,15 +1,59 @@ Certificates setup ------------------ -Certificates for API Users are generated via registnry admin user interface. -CSR must be uploaded for each API User. +Guide to setup all registry/epp/repp, webclient and api user certificates. + +There are three type of certificates: + +* root cert (one time action using command line) +* webclient server cert (one time action using command line) +* api user cert (multiple actions through admin interface) + +API users CSR are uploaded through registry admin interface for each API user. Private key and certificate must be packaged to pkcs12 and added to user browser. ### Registry setup -Setup CA directory tree: +Configure OpenSSL: + + sudo cp /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf.bak + sudo vi /etc/ssl/openssl.cnf + +Make sure the following options are in place: + + [ CA_default ] + # Where everything is kept + dir = /home/registry/registry/shared/ca # around line nr 42 + + crl_extensions = crl_ext # around line nr 71 + + # For the CA policy + [ policy_match ] + countryName = optional # around line nr 85 + stateOrProvinceName = optional # around line nr 86 + organizationName = optional # around line nr 87 + organizationalUnitName = optional # around line nr 88 + commonName = supplied # around line nr 89 + emailAddress = optional # around line nr 90 + + [ usr_cert ] + # These extensions are added when 'ca' signs a request. + basicConstraints=CA:FALSE # around line nr 170 + keyUsage = nonRepudiation, digitalSignature, keyEncipherment # around line nr 188 + nsComment = "OpenSSL Generated Certificate" # around line nr 191 + subjectKeyIdentifier=hash # around line nr 194 + authorityKeyIdentifier=keyid,issuer # around line nr 195 + + [ v3_ca ] + # Extensions for a typical CA + subjectKeyIdentifier=hash # around line nr 232 + authorityKeyIdentifier=keyid:always,issuer # around line nr 234 + basicConstraints = CA:true # around line nr 240 + keyUsage = cRLSign, keyCertSign # around line nr 245 + +Setup CA directory in shared directory: cd /home/registry/registry/shared mkdir ca @@ -20,73 +64,47 @@ Setup CA directory tree: echo 1000 > serial echo 1000 > crlnumber -Generate the root key (prompts for pass phrase): +Generate the root key and remember your password, you need it later in application.yml: openssl genrsa -aes256 -out private/ca.key.pem 4096 -Configure OpenSSL: +Create root registry certificate (prompts for additional data and review days flag): - sudo su - - cd /etc/ssl/ - cp openssl.cnf openssl.cnf.bak - nano openssl.cnf - exit - -Make sure the following options are in place: - - crl_extensions = crl_ext - - [ 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 - - # For the CA policy - [ policy_match ] - countryName = optional - stateOrProvinceName = optional - organizationName = optional - organizationalUnitName = optional - commonName = supplied - 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.crt.pem + openssl req -new -x509 -days 3653 -key private/ca.key.pem -sha256 -extensions v3_ca -out certs/ca.crt.pem chmod 444 certs/ca.crt.pem -Create a CSR for the webclient: +Create a webclient key and CSR for accepting webclient request: openssl genrsa -out private/webclient.key.pem 4096 chmod 400 private/webclient.key.pem - openssl req -sha256 -new -key private/webclient.key.pem -out csrs/webclient.csr.pem + openssl req -sha256 -new -days 3653 -key private/webclient.key.pem -out csrs/webclient.csr.pem -Sign the request and create certificate: +Sign CSR and create certificate: - openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -out certs/webclient.crt.pem + openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -extensions usr_cert -notext -md sha256 -in csrs/webclient.csr.pem -days 3653 -out certs/webclient.crt.pem chmod 444 certs/webclient.crt.pem Create certificate revocation list (prompts for pass phrase): openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem -Configure EPP virtual host: +Configure registry registry/shared/config/application.yml to match the CA settings: - sudo nano /etc/apache2/sites-enabled/epp.conf + ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' + ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' + ca_key_password: 'your-root-key-password' + crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' + + +### Registry EPP setup + +Configure registry epp registry-epp/shared/config/application.yml: + + webclient_ip: '54.154.91.240' + +Configure EPP port 700 virtual host: + + sudo vi /etc/apache2/sites-enabled/epp.conf Replace this line: @@ -102,16 +120,32 @@ With these lines: # SSLCARevocationCheck chain RequestHeader set SSL_CLIENT_S_DN_CN "%{SSL_CLIENT_S_DN_CN}s" +Reload apache: + + sudo a2enmod headers + sudo /etc/init.d/apache2 restart + + +### Webclient setup + +Copy all registry/shared/ca directory to your webclient server if webclient is in different server, +otherwise just point everything to your registry/shared/ca directory. + +Configure webclient/shared/config/application.yml to match the CA settings: + + cert_path: '/home/webclient/webclient/shared/ca/certs/webclient.crt.pem' + key_path: '/home/webclient/webclient/shared/ca/private/webclient.key.pem' + Configure webclient virtual host: - sudo nano /etc/apache2/sites-enabled/webclient.conf + sudo vi /etc/apache2/sites-enabled/webclient.conf Add these lines: SSLVerifyClient none SSLVerifyDepth 1 - SSLCACertificateFile /home/registry/registry/shared/ca/certs/ca.crt.pem - SSLCARevocationFile /home/registry/registry/shared/ca/crl/crl.pem + SSLCACertificateFile /home/webclient/webclient/shared/ca/certs/ca.crt.pem + SSLCARevocationFile /home/webclient/webclient/shared/ca/crl/crl.pem # Uncomment this when upgrading to apache 2.4: # SSLCARevocationCheck chain @@ -127,18 +161,6 @@ Reload apache: sudo a2enmod headers sudo /etc/init.d/apache2 restart -Configure registry and epp application.yml to match the CA settings: - - ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' - ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' - ca_key_password: 'registryalpha' - crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' - webclient_ip: '54.154.91.240' - -Configure webclient application.yml to match the CA settings: - - cert_path: '/home/registry/registry/shared/ca/certs/webclient.crt.pem' - key_path: '/home/registry/registry/shared/ca/private/webclient.key.pem' Development env --------------- From 22ec31e6bd0bc52d3ea67ab27de0aff64e524860 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 26 Feb 2015 13:37:27 +0200 Subject: [PATCH 62/97] Fix registrar address --- app/models/registrar.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 219e5d06d..2078226b2 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -23,7 +23,7 @@ class Registrar < ActiveRecord::Base end def address - [street, city, state, zip].reject(&:empty?).compact.join(', ') + [street, city, state, zip].reject(&:blank?).compact.join(', ') end def to_s From ba8274abdbd9e3ff474573fc9419cabf1fafb4e6 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 13:47:35 +0200 Subject: [PATCH 63/97] Rename certificate.md to certificates.md --- doc/{certificate.md => certificates.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{certificate.md => certificates.md} (100%) diff --git a/doc/certificate.md b/doc/certificates.md similarity index 100% rename from doc/certificate.md rename to doc/certificates.md From 35822ee4d5fcfc065c5bf88c2e3e7c31321125b1 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 26 Feb 2015 17:35:37 +0200 Subject: [PATCH 64/97] rake db:all:setup loads seeds data correctly --- lib/tasks/db.rake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 9f7a0a839..5000aa802 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -17,7 +17,12 @@ namespace :db do task setup: [:environment] do Rake::Task['db:all:create'].invoke Rake::Task['db:all:schema:load'].invoke + + ActiveRecord::Base.clear_all_connections! + ActiveRecord::Base.establish_connection(Rails.env.to_sym) + puts "\n---------------------------- Import seed ----------------------------------------\n" Rake::Task['db:seed'].invoke + puts "\n All done!\n\n" end desc 'Create all databases: registry, api_log and whois' @@ -42,12 +47,12 @@ namespace :db do # just in case we allow only drop test, comment it out please for temp return unless Rails.env.test? - puts "\n---------------------------- Drop main database ----------------------------------------\n" Rake::Task['db:drop'].invoke + conf = ActiveRecord::Base.configurations + puts "#{conf[Rails.env]['database']} dropped" other_databases.each do |name| begin - puts "\n---------------------------- #{name} dropped ----------------------------------------\n" ActiveRecord::Base.clear_all_connections! ActiveRecord::Base.establish_connection(name.to_sym) From 1be1c23b53a0b7941af82d2c1871bc53b3be6b9b Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 11:21:19 +0200 Subject: [PATCH 65/97] Updated db:all rake task --- lib/tasks/db.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 5000aa802..fd35f1f23 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -20,8 +20,8 @@ namespace :db do ActiveRecord::Base.clear_all_connections! ActiveRecord::Base.establish_connection(Rails.env.to_sym) - puts "\n---------------------------- Import seed ----------------------------------------\n" - Rake::Task['db:seed'].invoke + # puts "\n---------------------------- Import seed ----------------------------------------\n" + # Rake::Task['db:seed'].invoke puts "\n All done!\n\n" end From 25d6bc5ae6fb01bf903fc75db4893a656f808bd8 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 12:32:39 +0200 Subject: [PATCH 66/97] Style updates for certificate docs --- doc/certificates.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/certificates.md b/doc/certificates.md index bf222a5e2..aba420862 100644 --- a/doc/certificates.md +++ b/doc/certificates.md @@ -56,9 +56,8 @@ Make sure the following options are in place: Setup CA directory in shared directory: cd /home/registry/registry/shared - mkdir ca + mkdir ca ca/certs ca/crl ca/newcerts ca/private ca/csrs cd ca - mkdir certs crl newcerts private csrs chmod 700 private touch index.txt echo 1000 > serial @@ -90,10 +89,10 @@ Create certificate revocation list (prompts for pass phrase): Configure registry registry/shared/config/application.yml to match the CA settings: + crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' - ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' + ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' ca_key_password: 'your-root-key-password' - crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' ### Registry EPP setup From 061015e88ac5303373b79f349275248f5aa178ea Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 13:13:07 +0200 Subject: [PATCH 67/97] Updated webclient ip address at application-example file --- config/application-example.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/application-example.yml b/config/application-example.yml index f5b0bf2ac..e31d63bec 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -15,12 +15,13 @@ defaults: &defaults ca_key_password: 'your-root-key-password' # Used by EPP server - webclient_ip: '54.154.91.240' + webclient_ip: '127.0.0.1' development: <<: *defaults test: + webclient_ip: '127.0.0.1' # it should match to localhost ip address <<: *defaults alpha: From 527bc2a0dc7a2ef587576c0aab00b7e46413241d Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 13:42:59 +0200 Subject: [PATCH 68/97] Fix epp session test, now api_user honors active boolean correctly --- app/models/api_user.rb | 2 +- .../20150227113121_change_api_user_default_value.rb | 5 +++++ db/schema.rb | 8 ++++---- spec/epp/session_spec.rb | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20150227113121_change_api_user_default_value.rb diff --git a/app/models/api_user.rb b/app/models/api_user.rb index cd07282b7..d95ce8414 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -20,7 +20,7 @@ class ApiUser < User after_initialize :set_defaults def set_defaults return unless new_record? - self.active = true + self.active = true unless active_changed? end def registrar_typeahead diff --git a/db/migrate/20150227113121_change_api_user_default_value.rb b/db/migrate/20150227113121_change_api_user_default_value.rb new file mode 100644 index 000000000..614abb587 --- /dev/null +++ b/db/migrate/20150227113121_change_api_user_default_value.rb @@ -0,0 +1,5 @@ +class ChangeApiUserDefaultValue < ActiveRecord::Migration + def change + change_column_default :users, :active, nil + end +end diff --git a/db/schema.rb b/db/schema.rb index cb37d9166..4630543d3 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: 20150223104842) do +ActiveRecord::Schema.define(version: 20150227113121) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -640,19 +640,19 @@ ActiveRecord::Schema.define(version: 20150223104842) do t.datetime "created_at" t.datetime "updated_at" t.string "email" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.inet "current_sign_in_ip" t.inet "last_sign_in_ip" t.string "identity_code" t.integer "country_id" - t.string "roles", array: true + t.string "roles", array: true t.string "creator_str" t.string "updator_str" t.string "country_code" t.integer "registrar_id" - t.boolean "active", default: false + t.boolean "active" t.text "csr" t.text "crt" t.string "type" diff --git a/spec/epp/session_spec.rb b/spec/epp/session_spec.rb index 2da903652..e0e8afae9 100644 --- a/spec/epp/session_spec.rb +++ b/spec/epp/session_spec.rb @@ -35,6 +35,7 @@ describe 'EPP Session', epp: true do inactive = @epp_xml.session.login(clID: { value: 'inactive-user' }, pw: { value: 'ghyt9e4fu' }) response = epp_plain_request(inactive, :xml) + response[:msg].should == 'Authentication error; server closing connection' response[:result_code].should == '2501' end From 1e38c0f7d79656bf4c6a9e14a70b3ff10a51c8e0 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 14:08:13 +0200 Subject: [PATCH 69/97] Updated robot setup --- bin/docker-robot | 10 ---------- bin/robot | 10 ++++------ config/database-robot.yml | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) delete mode 100755 bin/docker-robot create mode 100644 config/database-robot.yml diff --git a/bin/docker-robot b/bin/docker-robot deleted file mode 100755 index fe41bd1f1..000000000 --- a/bin/docker-robot +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# -# For docker -# - -# cd to Rails root directory -cd "$(dirname "$0")"; cd .. - -bin/update-repo -bin/robot diff --git a/bin/robot b/bin/robot index 28fbde348..927bd10b0 100755 --- a/bin/robot +++ b/bin/robot @@ -16,14 +16,12 @@ export RAILS_ENV=test cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml -# create manually config/database.yml +cp config/database-robot.yml config/database.yml bundle install -RAILS_ENV=test bundle exec rake db:drop -RAILS_ENV=test bundle exec rake db:all:create -RAILS_ENV=test bundle exec rake db:all:schema:load -RAILS_ENV=test bundle exec rake db:seed +RAILS_ENV=test bundle exec rake db:all:drop +RAILS_ENV=test bundle exec rake db:all:setup RAILS_ENV=test bundle exec rake assets:precompile echo "GIT_LAST_COMMITS" @@ -36,7 +34,7 @@ RCODE=$? echo "END_OF_RUBOCOP_RESULTS" echo "TEST_RESULTS" -ROBOT=true bundle exec rake test +ROBOT=true bundle exec rake TCODE=$? echo "END_OF_TEST_RESULTS" diff --git a/config/database-robot.yml b/config/database-robot.yml new file mode 100644 index 000000000..37d357726 --- /dev/null +++ b/config/database-robot.yml @@ -0,0 +1,19 @@ +default: &default + host: localhost + adapter: postgresql + encoding: unicode + pool: 5 + username: test1 + password: test + +test: + <<: *default + database: registry_test + +whois_test: + <<: *default + database: registry_whois_test + +api_log_test: + <<: *default + database: registry_api_log_test From e238760cef436a7765ba4d54908eac233dcbd6e1 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 15:41:53 +0200 Subject: [PATCH 70/97] Rubocop syntax update --- config/initializers/initial_settings.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/initializers/initial_settings.rb b/config/initializers/initial_settings.rb index 84090c28d..5458b6d2e 100644 --- a/config/initializers/initial_settings.rb +++ b/config/initializers/initial_settings.rb @@ -1,4 +1,5 @@ -if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('settings') # otherwise rake not working 100% +# otherwise rake not working 100% +if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('settings') Setting.disclosure_name = true if Setting.disclosure_name.nil? Setting.disclosure_name = true if Setting.disclosure_name.nil? Setting.disclosure_org_name = true if Setting.disclosure_org_name.nil? From 54b53941966ef5d4d6422aca3d3b9d377930fd30 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 16:46:52 +0200 Subject: [PATCH 71/97] All required env settings are now checkeda and depricated APP_CONFIG --- .gitignore | 4 +- CHANGELOG.md | 5 +++ Gemfile | 3 ++ Gemfile.lock | 3 ++ app/controllers/epp/sessions_controller.rb | 2 +- app/models/api_user_deprecated.rb | 6 +-- app/models/certificate.rb | 20 +++++----- app/models/zonefile_setting.rb | 2 +- app/views/layouts/application.haml | 2 +- config/application-example.yml | 43 ++++++++-------------- config/initializers/app_config.rb | 2 - config/initializers/devise.rb | 2 +- config/initializers/env_required.rb | 13 +++++++ config/initializers/set_secret.rb | 1 + 14 files changed, 61 insertions(+), 47 deletions(-) delete mode 100644 config/initializers/app_config.rb create mode 100644 config/initializers/env_required.rb create mode 100644 config/initializers/set_secret.rb diff --git a/.gitignore b/.gitignore index b7ebd630e..67a64e8d5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ capybara-*.html /spec/tmp **.orig config/initializers/secret_token.rb -config/application.yml config/secrets.yml config/database.yml /export @@ -25,3 +24,6 @@ config/database.yml # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc + +# Ignore application configuration +/config/application.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e9fd7e7e8..cc0bd3e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +27.02.2015 + +* Simplified config/application-example.yml, + now system will check if all required settings are present in application.yml + 19.02.2015 * Cetrificate only enabled, please setup certificates following doc/certificate.md document. diff --git a/Gemfile b/Gemfile index 68a2b9571..e0bf28b53 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,9 @@ gem 'rails', '4.2.0' gem 'iso8601', '~> 0.8.2' # for dates and times gem 'hashie_rails', '~> 0.0.1' +# load env +gem 'figaro', '~> 1.1.0' + # model related gem 'pg', '~> 0.18.0' gem 'ransack', '~> 1.5.1' # for searching diff --git a/Gemfile.lock b/Gemfile.lock index b798b512b..478d7604a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -152,6 +152,8 @@ GEM i18n (~> 0.5) fastercsv (1.5.5) ffi (1.9.6) + figaro (1.1.0) + thor (~> 0.14) flay (2.4.0) ruby_parser (~> 3.0) sexp_processor (~> 4.0) @@ -459,6 +461,7 @@ DEPENDENCIES epp-xml (~> 0.10.4) fabrication (~> 2.12.2) faker (~> 1.3.0) + figaro (~> 1.1.0) grape (~> 0.10.1) guard (~> 2.6.1) guard-rails (~> 0.7.0) diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index f12b2e56d..181cc19ac 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -9,7 +9,7 @@ class Epp::SessionsController < EppController # rubocop: disable Metrics/CyclomaticComplexity def login cert_valid = true - if request.ip == APP_CONFIG['webclient_ip'] + if request.ip == ENV['webclient_ip'] @api_user = ApiUser.find_by(login_params) else if request.env['HTTP_SSL_CLIENT_S_DN_CN'] != login_params[:username] diff --git a/app/models/api_user_deprecated.rb b/app/models/api_user_deprecated.rb index f44719fbb..c809564ea 100644 --- a/app/models/api_user_deprecated.rb +++ b/app/models/api_user_deprecated.rb @@ -32,10 +32,10 @@ class ApiUserDeprecated < ActiveRecord::Base csr_file.rewind crt_file = Tempfile.new('client_crt') - _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ - -cert #{APP_CONFIG['ca_cert_path']} \ + _out, err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + -cert #{ENV['ca_cert_path']} \ -extensions usr_cert -notext -md sha256 \ - -in #{csr_file.path} -out #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") + -in #{csr_file.path} -out #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch") if err.match(/Data Base Updated/) crt_file.rewind diff --git a/app/models/certificate.rb b/app/models/certificate.rb index 30cbbe949..51d4ac5e4 100644 --- a/app/models/certificate.rb +++ b/app/models/certificate.rb @@ -33,7 +33,7 @@ class Certificate < ActiveRecord::Base @cached_status = EXPIRED end - crl = OpenSSL::X509::CRL.new(File.open(APP_CONFIG['crl_path']).read) + crl = OpenSSL::X509::CRL.new(File.open(ENV['crl_path']).read) return @cached_status unless crl.revoked.map(&:serial).include?(parsed_crt.serial) @cached_status = REVOKED @@ -45,10 +45,10 @@ class Certificate < ActiveRecord::Base csr_file.rewind crt_file = Tempfile.new('client_crt') - _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ - -cert #{APP_CONFIG['ca_cert_path']} \ + _out, err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + -cert #{ENV['ca_cert_path']} \ -extensions usr_cert -notext -md sha256 \ - -in #{csr_file.path} -out #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") + -in #{csr_file.path} -out #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch") if err.match(/Data Base Updated/) crt_file.rewind @@ -67,9 +67,9 @@ class Certificate < ActiveRecord::Base crt_file.write(crt) crt_file.rewind - _out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ - -cert #{APP_CONFIG['ca_cert_path']} \ - -revoke #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch") + _out, err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + -cert #{ENV['ca_cert_path']} \ + -revoke #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch") if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/) save! @@ -81,8 +81,8 @@ class Certificate < ActiveRecord::Base return false end - _out, _err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \ - -cert #{APP_CONFIG['ca_cert_path']} \ - -gencrl -out #{APP_CONFIG['crl_path']} -key '#{APP_CONFIG['ca_key_password']}' -batch") + _out, _err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + -cert #{ENV['ca_cert_path']} \ + -gencrl -out #{ENV['crl_path']} -key '#{ENV['ca_key_password']}' -batch") end end diff --git a/app/models/zonefile_setting.rb b/app/models/zonefile_setting.rb index cdeb8087d..c9cac6154 100644 --- a/app/models/zonefile_setting.rb +++ b/app/models/zonefile_setting.rb @@ -18,7 +18,7 @@ class ZonefileSetting < ActiveRecord::Base "select generate_zonefile('#{origin}')" )[0]['generate_zonefile'] - File.open("#{APP_CONFIG['zonefile_export_dir']}/#{filename}", 'w') { |f| f.write(zf) } + File.open("#{ENV['zonefile_export_dir']}/#{filename}", 'w') { |f| f.write(zf) } STDOUT << "#{Time.now.utc} - Successfully generated zonefile #{filename}\n" end diff --git a/app/views/layouts/application.haml b/app/views/layouts/application.haml index 05e513372..13ab2aab7 100644 --- a/app/views/layouts/application.haml +++ b/app/views/layouts/application.haml @@ -21,7 +21,7 @@ %span.icon-bar %span.icon-bar = link_to admin_dashboard_path, class: 'navbar-brand' do - = APP_CONFIG['app_name'] + = ENV['app_name'] - if unstable_env.present? .text-center %small{style: 'color: #0074B3;'}= unstable_env diff --git a/config/application-example.yml b/config/application-example.yml index e31d63bec..61f154688 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -1,34 +1,23 @@ -defaults: &defaults - app_name: .EE Registry - zonefile_export_dir: 'export/zonefiles' +# Application configuration values - # You can use `rake secret` to generate a secure secret key. - # Your secret key is used for verifying the integrity of signed cookies. - # If you change this key, all old signed cookies will become invalid! - secret_key_base: please-change-it-you-can-generate-it-with-rake-secret - devise_secret: please-change-it-you-can-generate-it-with-rake-secret +app_name: .EE Registry +zonefile_export_dir: 'export/zonefiles' - # Used by registry admin server: - crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' - ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' - ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' - ca_key_password: 'your-root-key-password' +# You can use `rake secret` to generate a secure secret key. +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +secret_key_base: please-change-it-you-can-generate-it-with-rake-secret +devise_secret: please-change-it-you-can-generate-it-with-rake-secret - # Used by EPP server - webclient_ip: '127.0.0.1' +# Used by admin server, you can leave those empty for when running EPP server: +crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' +ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' +ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' +ca_key_password: 'your-root-key-password' -development: - <<: *defaults +# Used only by EPP server, you can leave it empty when running admin server: +webclient_ip: '127.0.0.1' +# autotest config overwrites test: webclient_ip: '127.0.0.1' # it should match to localhost ip address - <<: *defaults - -alpha: - <<: *defaults - -staging: - <<: *defaults - -production: - <<: *defaults diff --git a/config/initializers/app_config.rb b/config/initializers/app_config.rb deleted file mode 100644 index d76066b1a..000000000 --- a/config/initializers/app_config.rb +++ /dev/null @@ -1,2 +0,0 @@ -APP_CONFIG = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env] -Registry::Application.config.secret_token = APP_CONFIG['secret_key_base'] diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 8bbe137e2..6415ada3d 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -4,7 +4,7 @@ Devise.setup do |config| # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing # confirmation, reset password and unlock tokens in the database. - config.secret_key = APP_CONFIG['devise_secret'] + config.secret_key = ENV['devise_secret'] # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, diff --git a/config/initializers/env_required.rb b/config/initializers/env_required.rb new file mode 100644 index 000000000..c79520166 --- /dev/null +++ b/config/initializers/env_required.rb @@ -0,0 +1,13 @@ +required = %w( + app_name + zonefile_export_dir + secret_key_base + devise_secret + crl_path + ca_cert_path + ca_key_path + ca_key_password + webclient_ip +) + +Figaro.require_keys(required) diff --git a/config/initializers/set_secret.rb b/config/initializers/set_secret.rb new file mode 100644 index 000000000..ed1dbae7f --- /dev/null +++ b/config/initializers/set_secret.rb @@ -0,0 +1 @@ +Registry::Application.config.secret_token = ENV['secret_key_base'] From 8a56a0f4e37c39cbd09f97452d136a66f12aed31 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Fri, 27 Feb 2015 16:52:42 +0200 Subject: [PATCH 72/97] Add notice to application-example.yml --- config/application-example.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application-example.yml b/config/application-example.yml index 61f154688..5021eb195 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -1,4 +1,4 @@ -# Application configuration values +# Be sure to restart your server when you modify settings. app_name: .EE Registry zonefile_export_dir: 'export/zonefiles' From 4be78896f3b5903dc7721943cbdf34b2b577e63a Mon Sep 17 00:00:00 2001 From: Priidik Vaikla Date: Sat, 28 Feb 2015 19:46:26 +0200 Subject: [PATCH 73/97] Fixed typo --- doc/epp/contact.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/epp/contact.md b/doc/epp/contact.md index 7bfe35468..d977c4e24 100644 --- a/doc/epp/contact.md +++ b/doc/epp/contact.md @@ -1,6 +1,6 @@ ## Contact related functions -Please study official Cantact Mapping protocol: +Please study official Contact Mapping protocol: http://tools.ietf.org/html/rfc5733 More info at http://en.wikipedia.org/wiki/Extensible_Provisioning_Protocol From 3d42cf97022e9c585a06a474bea84ed1aec0163e Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 15:51:57 +0200 Subject: [PATCH 74/97] Updated robot setup --- config/database-robot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/database-robot.yml b/config/database-robot.yml index 37d357726..b38a048a2 100644 --- a/config/database-robot.yml +++ b/config/database-robot.yml @@ -3,7 +3,7 @@ default: &default adapter: postgresql encoding: unicode pool: 5 - username: test1 + username: test password: test test: From f8c3b12d3c9f2126c0215d631a773c7e622eb274 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 15:54:21 +0200 Subject: [PATCH 75/97] Update robot setup --- bin/robot | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/robot b/bin/robot index 927bd10b0..a35480ceb 100755 --- a/bin/robot +++ b/bin/robot @@ -18,6 +18,7 @@ cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml cp config/database-robot.yml config/database.yml +gem bundle install bundle install RAILS_ENV=test bundle exec rake db:all:drop From dd6aaa3241c3eb751d1dc8bb4377c309917aa08a Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 15:55:13 +0200 Subject: [PATCH 76/97] robot setup fix --- bin/robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/robot b/bin/robot index a35480ceb..57047ad14 100755 --- a/bin/robot +++ b/bin/robot @@ -18,7 +18,7 @@ cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml cp config/database-robot.yml config/database.yml -gem bundle install +gem install bundle bundle install RAILS_ENV=test bundle exec rake db:all:drop From 0a4aacc5b6388d073ba0e1eaab30ed8c3bb0f2d1 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 16:04:53 +0200 Subject: [PATCH 77/97] robot update, removed bundler install --- bin/robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/robot b/bin/robot index 57047ad14..a9a26ad3c 100755 --- a/bin/robot +++ b/bin/robot @@ -18,7 +18,7 @@ cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml cp config/database-robot.yml config/database.yml -gem install bundle +# under jenkins use rbenv-plugin wrapper bundle install RAILS_ENV=test bundle exec rake db:all:drop From 3efb8b5a64cf257b222cf8bdf8e518087f0ce97c Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 16:16:15 +0200 Subject: [PATCH 78/97] Robot: add secret key example --- bin/robot | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/robot b/bin/robot index a9a26ad3c..88334b159 100755 --- a/bin/robot +++ b/bin/robot @@ -16,6 +16,7 @@ export RAILS_ENV=test cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml +cp config/secrets-example.yml config/secrets.yml cp config/database-robot.yml config/database.yml # under jenkins use rbenv-plugin wrapper From 5f848553a01fb36cd58dce8e8553e0c121b7f16e Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 16:29:01 +0200 Subject: [PATCH 79/97] Robot: brakeman less verbose now --- bin/robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/robot b/bin/robot index 88334b159..55ac07031 100755 --- a/bin/robot +++ b/bin/robot @@ -45,7 +45,7 @@ bundle exec bundle-audit update bundle exec bundle-audit BCODE=$? BCODE=0 # tmp -bundle exec brakeman +bundle exec brakeman -q echo "END_OF_SECURITY_RESULTS" # update code review From 7f1eb48b03c7db711b7bdfd1f1f6f9d6c23c647b Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 16:34:41 +0200 Subject: [PATCH 80/97] Readme update about rake db:all:setup --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 24740e064..f3a09cbb4 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,11 @@ Create registry database manually, example: Or create all databases: - rake db:all:setup # will create all databases and loads all schemas - rake db:all:create # creates all databases - rake db:all:schema:load # loads all schemas - rake db:all:schema:dump # dumps all schemas + rake db:all:setup # will create all databases, loads all schemas and seeds + # other similar tasks if needed later: + # rake db:all:create # creates all databases only + # rake db:all:schema:load # loads all schemas + # rake db:all:schema:dump # dumps all schemas Production install (database schema should be loaded and seeds should be present) From efcbe1ad997d19c7db5cd885f74de30cea16cae9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:12:56 +0200 Subject: [PATCH 81/97] Readme refactor --- README.md | 134 ++++------------------------------- doc/application_build_doc.md | 57 ++++++++++++++- doc/testing.md | 114 +++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 123 deletions(-) create mode 100644 doc/testing.md diff --git a/README.md b/README.md index f3a09cbb4..0380f6a75 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Installation ### Registry app -Usual Rails 4 app installation (rbenv install is under Debian build doc) +Registry based on Rails 4 installation (rbenv install is under Debian build doc) Manual demo install and database setup: @@ -32,45 +32,11 @@ Manual demo install and database setup: cd demo-registry rbenv local 2.2.0 bundle + cp config/application-example.yml config/application.yml # and edit it cp config/database-example.yml config/database.yml # and edit it - cp config/initializers/devise_secret_example.rb.txt config/initializers/devise_secret.rb # and edit + bundle exec rake db:all:setup # for production, please follow deployment howto bundle exec rake assets:precompile -Create registry database manually, example: - - create database registry_production owner registry encoding 'UTF-8' LC_COLLATE 'et_EE.utf8' LC_CTYPE 'et_EE.utf8' template template0; - rake db:schema:load - rake db:seeds - -Or create all databases: - - rake db:all:setup # will create all databases, loads all schemas and seeds - # other similar tasks if needed later: - # rake db:all:create # creates all databases only - # rake db:all:schema:load # loads all schemas - # rake db:all:schema:dump # dumps all schemas - -Production install (database schema should be loaded and seeds should be present) - - # at your local machine - git clone git@github.com:internetee/registry.git - cd registry - rbenv local 2.2.0 # more info about rbenv at debian doc - gem install mina - mina pr setup # one time, only creates missing directories - ssh registry - - # at your server - cd registry - cp current/config/application-example.yml shared/config/application.yml # and edit it - cp current/config/database-example.yml shared/config/database.yml # and edit it - - vi /etc/apache2/sites-enabled/registry.conf # add conf and all needed serts - vi /etc/apache2/sites-enabled/epp.conf # add epp conf, restart apache - exit - # at your local machine - mina pr deploy # this is command you use in every application code update - ### Apache with patched mod_epp (Debian 7/Ubuntu 14.04 LTS) sudo apt-get install apache2 @@ -187,11 +153,22 @@ All registry demo data can be found at: Initially you can use two type of users: admin users and EPP users. + ### Certificates setup * [Certificates setup](/doc/certificates.md) +### Deployment + +* [Application build and update](/doc/application_build_doc.md) + + +### Autotesting + +* [Testing](/doc/testing.md) + + ### EPP web client Please follow EPP web client readme: @@ -206,89 +183,6 @@ Please follow WHOIS server readme: https://github.com/internetee/whois -Deployment ----------- - -* [Debian build](/doc/debian_build_doc.md) -* [Application build and update](/doc/application_build_doc.md) - -CRON ----- - -Crontab can be setup after deploy. Jobs can be viewed [here](/config/schedule.rb). - - mina pr cron:setup # to update the crontab. - mina pr cron:clear # to clear crontab. - -Autotesting ------------ - -* Before running tests for the first time: `RAILS_ENV=test rake db:seed` -* Run tests: `rake` -* Run EPP tests: `rake test:epp` -* Run all but EPP tests: `rake test:other` - -To see internal errors while testing EPP - - unicorn -E test -p 8989 - rake spec:epp - -### Apache mod_epp autotesting/debugging - -Autotesting Apache mod_epp without Registry app. - - sudo apt-get install apache2-dbg - -Includes htpasswd command to generate authentication files - - sudo apt-get install apache2-utils - -For manual debugging purposes, standalone CGI scripts can be used: -This needs a static greeting file, so you will have to make /var/www writable. - -```apache - - - Options ExecCGI - SetHandler cgi-script - - - Listen 1700 - - - EPPEngine On - EPPCommandRoot /cgi-bin/epp/command - EPPSessionRoot /cgi-bin/epp/session - EPPErrorRoot /cgi-bin/epp/error - - Alias /cgi-bin/epp/session/hello /var/www/html/epp/session-hello - - Alias /cgi-bin/epp/session/login /usr/lib/cgi-bin/epp/session-login - Alias /cgi-bin/epp/session/logout /usr/lib/cgi-bin/epp/session-logout - Alias /cgi-bin/epp/error/schema /usr/lib/cgi-bin/epp/error-schema - Alias /cgi-bin/epp/command/create /usr/lib/cgi-bin/epp/create - Alias /cgi-bin/epp/command/info /usr/lib/cgi-bin/epp/info - - EPPAuthURI /epp/auth/login - - AuthType Basic - AuthName "EPP" - AuthUserFile /etc/apache2/htpasswd - require valid-user - - - -``` - - sudo a2enmod cgi - sudo a2enmod authn_file # will be used for non implicit authentication URIs - sudo htpasswd -c /etc/apache2/htpasswd test - Type "test" when prompted - cd /usr/lib/cgi-bin - mkdir epp - -Copy the files from $mod_epp/examples/cgis to /usr/lib/cgi-bin/epp - ## Code Status Alpha release status, only model tests: diff --git a/doc/application_build_doc.md b/doc/application_build_doc.md index bd817b08b..53c93415b 100644 --- a/doc/application_build_doc.md +++ b/doc/application_build_doc.md @@ -1,7 +1,48 @@ -### Application build and update +Application build and update +---------------------------- -For application deployment we are using faster [Mina](https://github.com/mina-deploy/mina) -instead of Capistrano. +### Debian setup + +* [Debian build](/doc/debian_build_doc.md) + + +### Certificates setup + +* [Certificates setup](/doc/certificates.md) + + +### Production env setup + +For production you probably would like to create databases to your locale, example: + + create database registry_production owner registry encoding 'UTF-8' LC_COLLATE 'et_EE.utf8' LC_CTYPE 'et_EE.utf8' template template0; + +Deploy overview: (database schema should be loaded and seeds should be present) + + # at your local machine + git clone git@github.com:internetee/registry.git + cd registry + rbenv local 2.2.0 # more info about rbenv at debian doc + gem install mina + mina pr setup # one time, only creates missing directories + ssh registry + + # at your server + cd registry + cp current/config/application-example.yml shared/config/application.yml # and edit it + cp current/config/database-example.yml shared/config/database.yml # and edit it + + vi /etc/apache2/sites-enabled/registry.conf # add conf and all needed serts + vi /etc/apache2/sites-enabled/epp.conf # add epp conf, restart apache + exit + # at your local machine + mina pr deploy # this is command you use in every application code update + + + +### Deploy script setup + +We recommend [Mina](https://github.com/mina-deploy/mina) instead of Capistrano for deployment. All deploy code locates at config/deploy.rb file. @@ -68,3 +109,13 @@ General rake and mina tips: rake -T # list all rake commands rake -T db # list all database related commands mina -T # list all mina deploy commands + + +CRON +---- + +Crontab can be setup after deploy. Jobs can be viewed [here](/config/schedule.rb). + + mina pr cron:setup # to update the crontab. + mina pr cron:clear # to clear crontab. + diff --git a/doc/testing.md b/doc/testing.md new file mode 100644 index 000000000..e7b56e0cb --- /dev/null +++ b/doc/testing.md @@ -0,0 +1,114 @@ +Testing +------- + +Setup test databases: + + RAILS_ENV=test rake db:all:setup + +Run basic test (no EPP tests): + + rake + + +Testing EPP +=========== + +In order to test EPP, you have to configure apache to handle EPP request correctly. + +### Apache site config + +First you should have mod_epp installed, please follow main README for doing it. + +Apache site config for autotest, add file to /etc/apache2/sites-enabled/epp-autotest.conf + +```apache + + Listen 701 + + SSLEngine on + SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + SSLCertificateFile /etc/apache2/ssl/apache.crt + SSLCertificateKeyFile /etc/apache2/ssl/apache.key + + SSLVerifyClient optional_no_ca + + EPPEngine On + EPPCommandRoot /proxy/command + EPPSessionRoot /proxy/session + EPPErrorRoot /proxy/error + EPPRawFrame raw_frame + + ProxyPass /proxy/ http://localhost:8989/epp/ + + EPPAuthURI implicit + EPPReturncodeHeader X-EPP-Returncode + + +``` + + + +* Run all tests with temp server running automatically on port 8989: + + rake test + + +Manual debugging +================ + +### Apache mod_epp manual debugging + +Debugging Apache mod_epp without Registry app. + + sudo apt-get install apache2-dbg + +Includes htpasswd command to generate authentication files + + sudo apt-get install apache2-utils + +For manual debugging purposes, standalone CGI scripts can be used: +This needs a static greeting file, so you will have to make /var/www writable. + +```apache + + + Options ExecCGI + SetHandler cgi-script + + + Listen 1700 + + + EPPEngine On + EPPCommandRoot /cgi-bin/epp/command + EPPSessionRoot /cgi-bin/epp/session + EPPErrorRoot /cgi-bin/epp/error + + Alias /cgi-bin/epp/session/hello /var/www/html/epp/session-hello + + Alias /cgi-bin/epp/session/login /usr/lib/cgi-bin/epp/session-login + Alias /cgi-bin/epp/session/logout /usr/lib/cgi-bin/epp/session-logout + Alias /cgi-bin/epp/error/schema /usr/lib/cgi-bin/epp/error-schema + Alias /cgi-bin/epp/command/create /usr/lib/cgi-bin/epp/create + Alias /cgi-bin/epp/command/info /usr/lib/cgi-bin/epp/info + + EPPAuthURI /epp/auth/login + + AuthType Basic + AuthName "EPP" + AuthUserFile /etc/apache2/htpasswd + require valid-user + + + +``` + + sudo a2enmod cgi + sudo a2enmod authn_file # will be used for non implicit authentication URIs + sudo htpasswd -c /etc/apache2/htpasswd test + Type "test" when prompted + cd /usr/lib/cgi-bin + mkdir epp + +Copy the files from $mod_epp/examples/cgis to /usr/lib/cgi-bin/epp + From f863ca03a0a635019f2e3dd92333f9e3b96d833f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:26:49 +0200 Subject: [PATCH 82/97] Sertificate readme refactor and updated application-example file --- config/application-example.yml | 4 ++++ doc/certificates.md | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/config/application-example.yml b/config/application-example.yml index 5021eb195..594522fd0 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -21,3 +21,7 @@ webclient_ip: '127.0.0.1' # autotest config overwrites test: webclient_ip: '127.0.0.1' # it should match to localhost ip address + ca_cert_path: '/var/lib/jenkins/workspace/registry/ca/certs/ca.crt.pem' + ca_key_path: '/var/lib/jenkins/workspace/registry/ca/private/ca.key.pem' + ca_key_password: 'test' + diff --git a/doc/certificates.md b/doc/certificates.md index aba420862..647c59164 100644 --- a/doc/certificates.md +++ b/doc/certificates.md @@ -16,6 +16,16 @@ Private key and certificate must be packaged to pkcs12 and added to user browser ### Registry setup +Setup CA directory in shared directory: + + cd /home/registry/registry/shared + mkdir ca ca/certs ca/crl ca/newcerts ca/private ca/csrs + cd ca + chmod 700 private + touch index.txt + echo 1000 > serial + echo 1000 > crlnumber + Configure OpenSSL: sudo cp /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf.bak @@ -53,16 +63,6 @@ Make sure the following options are in place: basicConstraints = CA:true # around line nr 240 keyUsage = cRLSign, keyCertSign # around line nr 245 -Setup CA directory in shared directory: - - cd /home/registry/registry/shared - mkdir ca ca/certs ca/crl ca/newcerts ca/private ca/csrs - cd ca - chmod 700 private - touch index.txt - echo 1000 > serial - echo 1000 > crlnumber - Generate the root key and remember your password, you need it later in application.yml: openssl genrsa -aes256 -out private/ca.key.pem 4096 From b2f5fc42e46e836a062f45bbe17415f1abf60a51 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:29:41 +0200 Subject: [PATCH 83/97] Robot added EPP tests --- bin/robot | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/robot b/bin/robot index 55ac07031..188545cbc 100755 --- a/bin/robot +++ b/bin/robot @@ -37,6 +37,7 @@ echo "END_OF_RUBOCOP_RESULTS" echo "TEST_RESULTS" ROBOT=true bundle exec rake +ROBOT=true bundle exec test TCODE=$? echo "END_OF_TEST_RESULTS" From f27b710d17730e1d9eab3d2a9183dcae17f267e9 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:31:40 +0200 Subject: [PATCH 84/97] Robot turn basic test off --- bin/robot | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/robot b/bin/robot index 188545cbc..bc6dccaf8 100755 --- a/bin/robot +++ b/bin/robot @@ -36,7 +36,10 @@ RCODE=$? echo "END_OF_RUBOCOP_RESULTS" echo "TEST_RESULTS" -ROBOT=true bundle exec rake +# basic test +# ROBOT=true bundle exec rake + +# all tests with EPP ROBOT=true bundle exec test TCODE=$? echo "END_OF_TEST_RESULTS" From 98c08244726f5e37a2eeea69f52b7b71d9f87ad1 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:37:57 +0200 Subject: [PATCH 85/97] Fixed robot, now all tests enabled --- bin/robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/robot b/bin/robot index bc6dccaf8..9134a9137 100755 --- a/bin/robot +++ b/bin/robot @@ -40,7 +40,7 @@ echo "TEST_RESULTS" # ROBOT=true bundle exec rake # all tests with EPP -ROBOT=true bundle exec test +ROBOT=true bundle exec rake test TCODE=$? echo "END_OF_TEST_RESULTS" From aa5cc83344b6d94f2532a3e73ce7cc988919a47d Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Mon, 2 Mar 2015 17:48:17 +0200 Subject: [PATCH 86/97] Updated application example file --- config/application-example.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/application-example.yml b/config/application-example.yml index 594522fd0..02d1cc7e2 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -21,6 +21,7 @@ webclient_ip: '127.0.0.1' # autotest config overwrites test: webclient_ip: '127.0.0.1' # it should match to localhost ip address + crl_path: '/var/lib/jenkins/workspace/registry/ca/crl/crl.pem' ca_cert_path: '/var/lib/jenkins/workspace/registry/ca/certs/ca.crt.pem' ca_key_path: '/var/lib/jenkins/workspace/registry/ca/private/ca.key.pem' ca_key_password: 'test' From 767f7bb6df71ef71630d9c565927e8b900e26f6e Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 3 Mar 2015 16:30:31 +0200 Subject: [PATCH 87/97] Added custom contact id support --- Guardfile | 10 +-- app/controllers/epp/contacts_controller.rb | 4 +- app/models/contact.rb | 22 +++++- app/models/epp/contact.rb | 17 ++++- app/models/registrar.rb | 29 ++++++-- .../20150303130729_add_code_to_registrar.rb | 6 ++ db/schema.rb | 5 +- doc/epp/contact.md | 3 +- spec/epp/contact_spec.rb | 38 ++++++++--- spec/epp/domain_spec.rb | 6 +- spec/fabricators/contact_fabricator.rb | 5 +- spec/models/contact_spec.rb | 67 +++++++++++++++++-- spec/models/registrar_spec.rb | 25 +++++++ 13 files changed, 195 insertions(+), 42 deletions(-) create mode 100644 db/migrate/20150303130729_add_code_to_registrar.rb diff --git a/Guardfile b/Guardfile index 358d58e80..e6d0a443a 100644 --- a/Guardfile +++ b/Guardfile @@ -3,11 +3,11 @@ group :red_green_refactor, halt_on_fail: true do # be sure you have apache2 configured to # accept EPP request on port 701, what proxy to 8989. # port and environment is just for correct notification, all is overwritten by CLI - guard :rails, port: 8989, environment: 'test' do - # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do - watch('Gemfile.lock') - watch(%r{^(config|lib)/.*}) - end + # guard :rails, port: 8989, environment: 'test' do + # # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do + # watch('Gemfile.lock') + # watch(%r{^(config|lib)/.*}) + # end guard :rspec, cmd: 'spring rspec', notification: false do watch(%r{^spec/.+_spec\.rb$}) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 5af05d9fd..d93916055 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -17,9 +17,7 @@ class Epp::ContactsController < EppController def create authorize! :create, Epp::Contact - - @contact = Epp::Contact.new(params[:parsed_frame]) - @contact.registrar = current_user.registrar + @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) if @contact.save render_epp_response '/epp/contacts/create' diff --git a/app/models/contact.rb b/app/models/contact.rb index 722e4de33..da8a3f651 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -22,7 +22,11 @@ class Contact < ActiveRecord::Base 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 } + validates :code, + uniqueness: { message: :epp_id_taken }, + format: { with: /\A[\w\-\:]*\Z/i }, + length: { maximum: 100 } + validate :ident_valid_format? delegate :street, to: :address @@ -99,15 +103,27 @@ class Contact < ActiveRecord::Base ident_type != IDENT_TYPE_BIC end - # generate random id for contact def generate_code - self.code = SecureRandom.hex(4) + self.code = SecureRandom.hex(4) if code.blank? end def generate_auth_info + return if @generate_auth_info_disabled self.auth_info = SecureRandom.hex(16) end + def disable_generate_auth_info! # needed for testing + @generate_auth_info_disabled = true + end + + def auth_info=(pw) + self[:auth_info] = pw if new_record? + end + + def code=(code) + self[:code] = code if new_record? + end + # Find a way to use self.domains with contact def domains_owned Domain.where(owner_contact_id: id) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 0a6c14020..d9df7f2ae 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -45,9 +45,22 @@ class Epp::Contact < Contact # rubocop: enable Metrics/PerceivedComplexity # rubocop: enable Metrics/CyclomaticComplexity - def new(frame) + def new(frame, registrar) return super if frame.blank? - super(attrs_from(frame)) + + custom_code = + if frame.css('id').present? + "#{registrar.code}:#{frame.css('id').text.parameterize}" + else + nil + end + + super( + attrs_from(frame).merge( + code: custom_code, + registrar: registrar + ) + ) end def legal_document_attrs(legal_frame) diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 2078226b2..e895273b3 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -9,10 +9,18 @@ class Registrar < ActiveRecord::Base validates :name, :reg_no, :country_code, :email, presence: true validates :name, :reg_no, uniqueness: true + validate :set_code, if: :new_record? after_save :touch_domains_version validates :email, :billing_email, format: /@/, allow_blank: true + class << self + def search_by_query(query) + res = search(name_or_reg_no_cont: query).result + res.reduce([]) { |o, v| o << { id: v[:id], display_key: "#{v[:name]} (#{v[:reg_no]})" } } + end + end + def domain_transfers at = DomainTransfer.arel_table DomainTransfer.where( @@ -34,10 +42,23 @@ class Registrar < ActiveRecord::Base Country.new(country_code) end - class << self - def search_by_query(query) - res = search(name_or_reg_no_cont: query).result - res.reduce([]) { |o, v| o << { id: v[:id], display_key: "#{v[:name]} (#{v[:reg_no]})" } } + def code=(code) + self[:code] = code if new_record? + end + + private + + def set_code + return false if name.blank? + new_code = name.parameterize + + # ensure code is always uniq automatically for a new record + seq = 1 + while self.class.find_by_code(new_code) + new_code += seq.to_s + seq += 1 end + + self.code = new_code end end diff --git a/db/migrate/20150303130729_add_code_to_registrar.rb b/db/migrate/20150303130729_add_code_to_registrar.rb new file mode 100644 index 000000000..6dea363fe --- /dev/null +++ b/db/migrate/20150303130729_add_code_to_registrar.rb @@ -0,0 +1,6 @@ +class AddCodeToRegistrar < ActiveRecord::Migration + def change + add_column :registrars, :code, :string + add_index :registrars, :code + end +end diff --git a/db/schema.rb b/db/schema.rb index 4630543d3..02859bae8 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: 20150227113121) do +ActiveRecord::Schema.define(version: 20150303130729) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -611,8 +611,11 @@ ActiveRecord::Schema.define(version: 20150227113121) do t.string "city" t.string "street" t.string "zip" + t.string "code" end + add_index "registrars", ["code"], name: "index_registrars_on_code", using: :btree + create_table "reserved_domains", force: :cascade do |t| t.string "name" t.datetime "created_at" diff --git a/doc/epp/contact.md b/doc/epp/contact.md index d977c4e24..db136c0db 100644 --- a/doc/epp/contact.md +++ b/doc/epp/contact.md @@ -13,6 +13,7 @@ Contact Mapping protocol short version: ----------------------- ------- ----------------- 1 1 Attribute: xmlns:contact="urn:ietf:params:xml:ns:contact-1.0" + 0-1 Contact id, optional, generated automatically if missing 1 Postal information container 1 Full name of the contact 0-1 Name of organization @@ -42,7 +43,7 @@ Contact Mapping protocol short version: ----------------------- ------- ----------------- 1 1 Attribute: xmlns:contact="urn:ietf:params:xml:ns:contact-1.0" - 1 contact id, required + 1 Contact id, required 1 Change container 1 Postal information container 0-1 Full name of the contact diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index a025197c6..78085eed6 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -13,10 +13,8 @@ describe 'EPP Contact', epp: true do login_as :registrar1 - Contact.skip_callback(:create, :before, :generate_code) - Contact.skip_callback(:create, :before, :generate_auth_info) - @contact = Fabricate(:contact, registrar: @registrar1) + @legal_document = { legalDocument: { value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', @@ -25,11 +23,6 @@ describe 'EPP Contact', epp: true do } 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 = {}) @@ -133,6 +126,17 @@ describe 'EPP Contact', epp: true do # 5 seconds for what-ever weird lag reasons might happen cr_date.text.to_time.should be_within(5).of(Time.now) end + + it 'successfully saves custom code' do + response = create_request( + { id: { value: '12345' } } + ) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + Contact.last.code.should == 'registrar1:12345' + end end context 'update command' do @@ -140,11 +144,9 @@ describe 'EPP Contact', epp: true do @contact = Fabricate( :contact, - # created_by_id: 1, registrar: @registrar1, email: 'not_updated@test.test', - code: 'sh8013', - auth_info: 'password' + code: 'sh8013' ) end @@ -226,6 +228,20 @@ describe 'EPP Contact', epp: true do response[:results][1][:msg].should == 'Email is invalid' response[:results][1][:result_code].should == '2005' end + + it 'should not update code with custom string' do + response = update_request( + id: { value: 'sh8013' }, + chg: { + id: { value: 'notpossibletoupdate' } + } + ) + + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + + @contact.reload.code.should == 'sh8013' + end end context 'delete command' do diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index cf49a0987..18249e471 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -11,8 +11,6 @@ describe 'EPP Domain', epp: true do login_as :registrar1 - Contact.skip_callback(:create, :before, :generate_code) - Fabricate(:contact, code: 'citizen_1234') Fabricate(:contact, code: 'sh8013') Fabricate(:contact, code: 'sh801333') @@ -254,8 +252,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:result_code].should == '2005' response[:msg].should == 'Hostname is invalid' + response[:result_code].should == '2005' end it 'checks hostAttr presence' do @@ -271,8 +269,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:result_code].should == '2003' response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr' + response[:result_code].should == '2003' end it 'creates domain with nameservers with ips' do diff --git a/spec/fabricators/contact_fabricator.rb b/spec/fabricators/contact_fabricator.rb index 0462f7a61..45c4db75e 100644 --- a/spec/fabricators/contact_fabricator.rb +++ b/spec/fabricators/contact_fabricator.rb @@ -1,13 +1,16 @@ Fabricator(:contact) do code { "sh#{Faker::Number.number(8)}" } + auth_info 'password' name { sequence(:name) { |i| "#{Faker::Name.name}#{i}" } } phone '+372.12345678' email Faker::Internet.email ident '37605030299' ident_type 'priv' ident_country_code 'EE' - auth_info 'ccds4324pok' address registrar { Fabricate(:registrar, name: Faker::Company.name, reg_no: Faker::Company.duns_number) } disclosure { Fabricate(:contact_disclosure) } + # rubocop: disable Style/SymbolProc + after_validation { |c| c.disable_generate_auth_info! } + # rubocop: enamble Style/SymbolProc end diff --git a/spec/models/contact_spec.rb b/spec/models/contact_spec.rb index d605e9781..dbf7bc3dc 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -91,6 +91,12 @@ describe Contact do it 'should not have any versions' do @contact.versions.should == [] end + + it 'should not accept long code' do + @contact.code = 'verylongcode' * 100 + @contact.valid? + @contact.errors[:code].should == ['is too long (maximum is 100 characters)'] + end end context 'with valid attributes' do @@ -130,6 +136,17 @@ describe Contact do @contact.errors.full_messages.should match_array([]) end + it 'should not accept new custom code' do + old_code = @contact.code + @contact.code = 'CID:REG1:12345' + @contact.save.should == true + @contact.code.should == old_code + end + + it 'should have static password' do + @contact.auth_info.should == 'password' + end + context 'as birthday' do before :all do @contact.ident_type = 'birthday' @@ -182,20 +199,56 @@ describe Contact do end context 'after create' do - it 'should generate a new code and password' do + it 'should not generate a new code when code is present' do + @contact = Fabricate.build(:contact, code: '123asd', auth_info: 'qwe321') + @contact.code.should == '123asd' + @contact.save.should == true + @contact.code.should == '123asd' + end + + it 'should generate a new password' do @contact = Fabricate.build(:contact, code: '123asd', auth_info: 'qwe321') - @contact.code.should == '123asd' @contact.auth_info.should == 'qwe321' - @contact.save! - @contact.code.should_not == '123asd' + @contact.save.should == true @contact.auth_info.should_not == 'qwe321' end + + it 'should not allow same code' do + @double_contact = Fabricate.build(:contact, code: @contact.code) + @double_contact.valid? + @double_contact.errors.full_messages.should == ["Code Contact id already exists"] + end + + it 'should allow supported code format' do + @contact = Fabricate.build(:contact, code: 'CID:REG1:12345') + @contact.valid? + @contact.errors.full_messages.should == [] + end + + it 'should not allow unsupported characters in code' do + @contact = Fabricate.build(:contact, code: 'unsupported!ÄÖÜ~?') + @contact.valid? + @contact.errors.full_messages.should == ['Code is invalid'] + end + + it 'should generate code if empty code is given' do + @contact = Fabricate(:contact, code: '') + @contact.code.should_not == '' + end + + it 'should not allow empty spaces as code' do + @contact = Fabricate.build(:contact, code: ' ') + @contact.valid? + @contact.errors.full_messages.should == ['Code is invalid'] + end end context 'after update' do before :all do - @contact.code = '123asd' - @contact.auth_info = 'qwe321' + @contact = Fabricate.build(:contact, code: '123asd', auth_info: 'qwe321') + @contact.save + @contact.code.should == '123asd' + @auth_info = @contact.auth_info end it 'should not generate new code' do @@ -205,7 +258,7 @@ describe Contact do it 'should not generate new auth_info' do @contact.update_attributes(name: 'fvrsgbqevciherot23') - @contact.auth_info.should == 'qwe321' + @contact.auth_info.should == @auth_info end end end diff --git a/spec/models/registrar_spec.rb b/spec/models/registrar_spec.rb index 7271a307d..4c92e5f66 100644 --- a/spec/models/registrar_spec.rb +++ b/spec/models/registrar_spec.rb @@ -28,6 +28,10 @@ describe Registrar do @registrar.errors[:email].should == ['is invalid'] @registrar.errors[:billing_email].should == ['is invalid'] end + + it 'should not have valid code' do + @registrar.code.should == nil + end end context 'with valid attributes' do @@ -59,5 +63,26 @@ describe Registrar do it 'should return full address' do @registrar.address.should == 'Street 999, Town, County, Postal' end + + it 'should have code' do + @registrar.code.should =~ /registrar/ + end + + it 'should not be able to change code' do + @registrar.code = 'not-updated' + @registrar.code.should =~ /registrar/ + end + + it 'should automatically add next code if original is taken' do + @registrar = Fabricate(:registrar, name: 'uniq') + @registrar.name = 'New name' + @registrar.code.should == 'uniq' + @registrar.save + + @new_registrar = Fabricate.build(:registrar, name: 'uniq') + @new_registrar.valid? + @new_registrar.errors.full_messages.should == [] + @new_registrar.code.should == 'uniq1' + end end end From 980f30b8debdd7122a05d5f49111162f1655af75 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Tue, 3 Mar 2015 17:15:18 +0200 Subject: [PATCH 88/97] Registrar code data updates --- .../20150303151224_data_update_regisntrar_codes.rb | 11 +++++++++++ db/schema.rb | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150303151224_data_update_regisntrar_codes.rb diff --git a/db/migrate/20150303151224_data_update_regisntrar_codes.rb b/db/migrate/20150303151224_data_update_regisntrar_codes.rb new file mode 100644 index 000000000..40d2b31cd --- /dev/null +++ b/db/migrate/20150303151224_data_update_regisntrar_codes.rb @@ -0,0 +1,11 @@ +class DataUpdateRegisntrarCodes < ActiveRecord::Migration + def change + puts 'Registrar code updates:' + Registrar.all.each do |r| + next if r.code.present? + r[:code] = r.name.parameterize + puts "#{r.id}: #{r.changes.inspect}" + r.save! + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 02859bae8..fdedb6f22 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: 20150303130729) do +ActiveRecord::Schema.define(version: 20150303151224) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From ac6361df05032daccaafecbae2d792422991a851 Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 4 Mar 2015 11:41:45 +0200 Subject: [PATCH 89/97] Added api user p12 readme --- doc/certificates.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc/certificates.md b/doc/certificates.md index 647c59164..ea28a22f6 100644 --- a/doc/certificates.md +++ b/doc/certificates.md @@ -161,6 +161,29 @@ Reload apache: sudo /etc/init.d/apache2 restart +### ApiUser browser setup + +In short: + +* Upload CSR file to api user at admin page /admin/api_users +* Sign it +* Generate p12 file and install into user browser + +#### Creating CSR file + + openssl genrsa -out private/api-user.key.pem 4096 + chmod 400 private/api-user.key.pem + openssl req -sha256 -new -days 3653 -key private/api-user.key.pem -out csrs/api-user.csr.pem + +Upload api-user.csr.pem file to api user at admin interface. +Sign it +Download CRT file and create p12 file. + + openssl pkcs12 -export -inkey private/api-user.key.pem -in certs/api-user.crt.pem -out pkcs/api_user.p12 + +Add api_user.p12 to your browser. + + Development env --------------- From fdac633f07ff2cdc3892b37a6b5ffd1db594eada Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Wed, 4 Mar 2015 17:15:43 +0200 Subject: [PATCH 90/97] Refactor epp_errors and add attribute to msg --- app/models/concerns/epp_errors.rb | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/models/concerns/epp_errors.rb b/app/models/concerns/epp_errors.rb index 4d95cea27..c3102af19 100644 --- a/app/models/concerns/epp_errors.rb +++ b/app/models/concerns/epp_errors.rb @@ -3,42 +3,42 @@ module EppErrors def construct_epp_errors epp_errors = [] - errors.messages.each do |key, values| - key = key.to_s.split('.')[0].to_sym - next if key == :epp_errors + errors.messages.each do |attr, errors| + attr = attr.to_s.split('.')[0].to_sym + next if attr == :epp_errors - if self.class.reflect_on_association(key) - epp_errors << collect_child_errors(key) + if self.class.reflect_on_association(attr) + epp_errors << collect_child_errors(attr) end - epp_errors << collect_parent_errors(values) + epp_errors << collect_parent_errors(attr, errors) end errors[:epp_errors] = epp_errors errors[:epp_errors].flatten! end - def collect_parent_errors(values) - epp_errors = [] - values = [values] if values.is_a?(String) + def collect_parent_errors(attr, errors) + errors = [errors] if errors.is_a?(String) - values.each do |err| + epp_errors = [] + errors.each do |err| code, value = find_epp_code_and_value(err) next unless code - epp_errors << { code: code, msg: err, value: value } + epp_errors << { code: code, msg: "#{err} [#{attr}]", value: value } end epp_errors end - def collect_child_errors(key) - macro = self.class.reflect_on_association(key).macro + def collect_child_errors(attr) + macro = self.class.reflect_on_association(attr).macro multi = [:has_and_belongs_to_many, :has_many] # single = [:belongs_to, :has_one] epp_errors = [] - send(key).each do |x| - x.errors.messages.each do |_key, values| - epp_errors << x.collect_parent_errors(values) + send(attr).each do |x| + x.errors.messages.each do |attribute, errors| + epp_errors << x.collect_parent_errors(attribute, errors) end end if multi.include?(macro) From bca9ddef16f35a372075c8be9a4f4b3e5c20401f Mon Sep 17 00:00:00 2001 From: Priit Tark Date: Thu, 5 Mar 2015 11:06:55 +0200 Subject: [PATCH 91/97] Attribute is added to error messages --- app/controllers/epp_controller.rb | 3 +- app/models/concerns/epp_errors.rb | 3 +- spec/epp/contact_spec.rb | 36 ++++++------- spec/epp/domain_spec.rb | 89 +++++++++++++++++-------------- spec/epp/keyrelay_spec.rb | 2 +- 5 files changed, 72 insertions(+), 61 deletions(-) diff --git a/app/controllers/epp_controller.rb b/app/controllers/epp_controller.rb index 89623117c..a0985be4a 100644 --- a/app/controllers/epp_controller.rb +++ b/app/controllers/epp_controller.rb @@ -103,6 +103,7 @@ class EppController < ApplicationController el, missing = nil, nil selectors.each do |selector| full_selector = [@prefix, selector].compact.join(' ') + attr = selector.split('>').last.strip.underscore el = params[:parsed_frame].css(full_selector).first if allow_blank @@ -112,7 +113,7 @@ class EppController < ApplicationController end epp_errors << { code: '2003', - msg: I18n.t('errors.messages.required_parameter_missing', key: full_selector) + msg: I18n.t('errors.messages.required_parameter_missing', key: "#{full_selector} [#{attr}]") } if missing end diff --git a/app/models/concerns/epp_errors.rb b/app/models/concerns/epp_errors.rb index c3102af19..cf3824260 100644 --- a/app/models/concerns/epp_errors.rb +++ b/app/models/concerns/epp_errors.rb @@ -25,7 +25,8 @@ module EppErrors errors.each do |err| code, value = find_epp_code_and_value(err) next unless code - epp_errors << { code: code, msg: "#{err} [#{attr}]", value: value } + msg = attr.to_sym == :base ? err : "#{err} [#{attr}]" + epp_errors << { code: code, msg: msg, value: value } end epp_errors end diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 78085eed6..4690acfed 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -46,19 +46,19 @@ describe 'EPP Contact', epp: true do 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' + 'Required parameter missing: create > create > postalInfo > name [name]' response[:results][1][:msg].should == - 'Required parameter missing: create > create > postalInfo > addr > city' + 'Required parameter missing: create > create > postalInfo > addr > city [city]' response[:results][2][:msg].should == - 'Required parameter missing: create > create > postalInfo > addr > cc' + 'Required parameter missing: create > create > postalInfo > addr > cc [cc]' response[:results][3][:msg].should == - 'Required parameter missing: create > create > ident' + 'Required parameter missing: create > create > ident [ident]' response[:results][4][:msg].should == - 'Required parameter missing: create > create > voice' + 'Required parameter missing: create > create > voice [voice]' response[:results][5][:msg].should == - 'Required parameter missing: create > create > email' + 'Required parameter missing: create > create > email [email]' response[:results][6][:msg].should == - 'Required parameter missing: extension > extdata > legalDocument' + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' response[:results][0][:result_code].should == '2003' response[:results][1][:result_code].should == '2003' @@ -179,13 +179,13 @@ describe 'EPP Contact', epp: true do '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' + 'Required parameter missing: update > update > id [id]' response[:results][1][:result_code].should == '2003' response[:results][2][:msg].should == - 'Required parameter missing: update > update > authInfo > pw' + 'Required parameter missing: update > update > authInfo > pw [pw]' response[:results][2][:result_code].should == '2003' response[:results][3][:msg].should == - 'Required parameter missing: extension > extdata > legalDocument' + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' response[:results][3][:result_code].should == '2003' response[:results].count.should == 4 end @@ -223,9 +223,9 @@ describe 'EPP Contact', epp: true do } }) - response[:results][0][:msg].should == 'Phone nr is invalid' + response[:results][0][:msg].should == 'Phone nr is invalid [phone]' response[:results][0][:result_code].should == '2005' - response[:results][1][:msg].should == 'Email is invalid' + response[:results][1][:msg].should == 'Email is invalid [email]' response[:results][1][:result_code].should == '2005' end @@ -262,13 +262,13 @@ describe 'EPP Contact', epp: true do response = epp_plain_request(@epp_xml.delete, :xml) response[:results][0][:msg].should == - 'Required parameter missing: delete > delete > id' + 'Required parameter missing: delete > delete > id [id]' response[:results][0][:result_code].should == '2003' response[:results][1][:msg].should == - 'Required parameter missing: delete > delete > authInfo > pw' + 'Required parameter missing: delete > delete > authInfo > pw [pw]' response[:results][1][:result_code].should == '2003' response[:results][2][:msg].should == - 'Required parameter missing: extension > extdata > legalDocument' + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' response[:results][2][:result_code].should == '2003' response[:results].count.should == 3 end @@ -294,7 +294,7 @@ describe 'EPP Contact', epp: true do @domain.owner_contact.address.present?.should == true response = delete_request - response[:msg].should == 'Object association prohibits operation' + response[:msg].should == 'Object association prohibits operation [domains]' response[:result_code].should == '2305' response[:results].count.should == 1 @@ -324,7 +324,7 @@ describe 'EPP Contact', epp: true do 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][:msg].should == 'Required parameter missing: check > check > id [id]' response[:results][0][:result_code].should == '2003' response[:results].count.should == 1 end @@ -359,7 +359,7 @@ describe 'EPP Contact', epp: true do 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' + 'Required parameter missing: info > info > id [id]' response[:results][0][:result_code].should == '2003' response[:results].count.should == 1 end diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 18249e471..1b03e2487 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -59,16 +59,20 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2003' - response[:results][0][:msg].should == 'Required parameter missing: create > create > ns' + response[:results][0][:msg].should == + 'Required parameter missing: create > create > ns [ns]' response[:results][1][:result_code].should == '2003' - response[:results][1][:msg].should == 'Required parameter missing: create > create > registrant' + response[:results][1][:msg].should == + 'Required parameter missing: create > create > registrant [registrant]' response[:results][2][:result_code].should == '2003' - response[:results][2][:msg].should == 'Required parameter missing: create > create > ns > hostAttr' + response[:results][2][:msg].should == + 'Required parameter missing: create > create > ns > hostAttr [host_attr]' response[:results][3][:result_code].should == '2003' - response[:results][3][:msg].should == 'Required parameter missing: extension > extdata > legalDocument' + response[:results][3][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' end context 'with citizen as an owner' do @@ -167,7 +171,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2306' - response[:msg].should == 'IPv4 is missing' + response[:msg].should == 'IPv4 is missing [ipv4]' end # it 'does not create duplicate domain' do @@ -189,7 +193,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2302' - response[:msg].should == 'Domain name is reserved or restricted' + response[:msg].should == 'Domain name is reserved or restricted [name_dirty]' response[:clTRID].should == 'ABC-12345' end @@ -198,7 +202,8 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2003' - response[:results][0][:msg].should == 'Required parameter missing: create > create > registrant' + response[:results][0][:msg].should == + 'Required parameter missing: create > create > registrant [registrant]' end it 'does not create domain without nameservers' do @@ -206,11 +211,11 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:msg].should == - 'Required parameter missing: create > create > ns' + 'Required parameter missing: create > create > ns [ns]' response[:results][0][:result_code].should == '2003' response[:results][1][:msg].should == - 'Required parameter missing: create > create > ns > hostAttr' + 'Required parameter missing: create > create > ns > hostAttr [host_attr]' response[:results][1][:result_code].should == '2003' response[:results].count.should == 2 @@ -232,7 +237,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2004' - response[:msg].should == 'Nameservers count must be between 2-11' + response[:msg].should == 'Nameservers count must be between 2-11 [nameservers]' end it 'returns error when invalid nameservers are present' do @@ -252,7 +257,7 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:msg].should == 'Hostname is invalid' + response[:msg].should == 'Hostname is invalid [hostname]' response[:result_code].should == '2005' end @@ -269,7 +274,7 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr' + response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr [host_attr]' response[:result_code].should == '2003' end @@ -286,10 +291,10 @@ describe 'EPP Domain', epp: true do nameserver_count = Nameserver.count response = epp_plain_request(domain_create_with_invalid_ns_ip_xml, :xml) response[:results][0][:result_code].should == '2005' - response[:results][0][:msg].should == 'IPv4 is invalid' + response[:results][0][:msg].should == 'IPv4 is invalid [ipv4]' response[:results][0][:value].should == '192.0.2.2.invalid' response[:results][1][:result_code].should == '2005' - response[:results][1][:msg].should == 'IPv6 is invalid' + response[:results][1][:msg].should == 'IPv6 is invalid [ipv6]' response[:results][1][:value].should == 'INVALID_IPV6' # ensure nothing gets saved to db: Domain.count.should == domain_count @@ -312,7 +317,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2004' - response[:results][0][:msg].should == 'Period must add up to 1, 2 or 3 years' + response[:results][0][:msg].should == 'Period must add up to 1, 2 or 3 years [period]' response[:results][0][:value].should == '367' end @@ -397,24 +402,25 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) - response[:results][0][:msg].should == 'Valid algorithms are: 3, 5, 6, 7, 8, 252, 253, 254, 255' + response[:results][0][:msg].should == + 'Valid algorithms are: 3, 5, 6, 7, 8, 252, 253, 254, 255 [alg]' response[:results][0][:value].should == '9' - response[:results][1][:msg].should == 'Valid protocols are: 3' + response[:results][1][:msg].should == 'Valid protocols are: 3 [protocol]' response[:results][1][:value].should == '4' - response[:results][2][:msg].should == 'Valid flags are: 0, 256, 257' + response[:results][2][:msg].should == 'Valid flags are: 0, 256, 257 [flags]' response[:results][2][:value].should == '250' - response[:results][3][:msg].should == 'Valid algorithms are: 3, 5, 6, 7, 8, 252, 253, 254, 255' + response[:results][3][:msg].should == 'Valid algorithms are: 3, 5, 6, 7, 8, 252, 253, 254, 255 [alg]' response[:results][3][:value].should == '10' - response[:results][4][:msg].should == 'Valid flags are: 0, 256, 257' + response[:results][4][:msg].should == 'Valid flags are: 0, 256, 257 [flags]' response[:results][4][:value].should == '1' - response[:results][5][:msg].should == 'Public key is missing' + response[:results][5][:msg].should == 'Public key is missing [public_key]' - response[:results][6][:msg].should == 'Valid protocols are: 3' + response[:results][6][:msg].should == 'Valid protocols are: 3 [protocol]' response[:results][6][:value].should == '5' end @@ -441,7 +447,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2302' - response[:msg].should == 'Public key already exists' + response[:msg].should == 'Public key already exists [public_key]' response[:results][0][:value].should == '700b97b591ed27ec2590d19f06f88bba700b97b591ed27ec2590d19f' end @@ -470,7 +476,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2004' - response[:msg].should == 'DNS keys count must be between 0-1' + response[:msg].should == 'DNS keys count must be between 0-1 [dnskeys]' create_settings end @@ -667,7 +673,7 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) - response[:msg].should == 'Admin contacts count must be between 1-10' + response[:msg].should == 'Admin contacts count must be between 1-10 [admin_contacts]' response[:result_code].should == '2004' response[:clTRID].should == 'ABC-12345' @@ -859,7 +865,8 @@ describe 'EPP Domain', epp: true do login_as :registrar2 do response = epp_plain_request(xml, :xml) response[:result_code].should == '2003' - response[:msg].should == 'Required parameter missing: extension > extdata > legalDocument' + response[:msg].should == + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' end end @@ -976,7 +983,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:result_code].should == '2201' - response[:msg].should == 'Authorization error' + response[:msg].should == 'Authorization error [auth_info]' end it 'ignores transfer when owner registrar requests transfer' do @@ -1023,7 +1030,7 @@ describe 'EPP Domain', epp: true do epp_plain_request(xml, :xml) # transfer domain response = epp_plain_request(xml, :xml) # attempt second transfer response[:result_code].should == '2201' - response[:msg].should == 'Authorization error' + response[:msg].should == 'Authorization error [auth_info]' end end @@ -1126,27 +1133,27 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2302' - response[:results][0][:msg].should == 'Nameserver already exists on this domain' + response[:results][0][:msg].should == 'Nameserver already exists on this domain [hostname]' response[:results][0][:value].should == 'ns1.example.com' response[:results][1][:result_code].should == '2302' - response[:results][1][:msg].should == 'Nameserver already exists on this domain' + response[:results][1][:msg].should == 'Nameserver already exists on this domain [hostname]' response[:results][1][:value].should == 'ns2.example.com' response[:results][2][:result_code].should == '2302' - response[:results][2][:msg].should == 'Contact already exists on this domain' + response[:results][2][:msg].should == 'Contact already exists on this domain [contact_code_cache]' response[:results][2][:value].should == 'mak21' - response[:results][3][:msg].should == 'Status already exists on this domain' + response[:results][3][:msg].should == 'Status already exists on this domain [value]' response[:results][3][:value].should == 'clientHold' - response[:results][4][:msg].should == 'Status already exists on this domain' + response[:results][4][:msg].should == 'Status already exists on this domain [value]' response[:results][4][:value].should == 'clientUpdateProhibited' - response[:results][5][:msg].should == 'Public key already exists' + response[:results][5][:msg].should == 'Public key already exists [public_key]' response[:results][5][:value].should == '700b97b591ed27ec2590d19f06f88bba700b97b591ed27ec2590d19f' - response[:results][6][:msg].should == 'Public key already exists' + response[:results][6][:msg].should == 'Public key already exists [public_key]' response[:results][6][:value].should == '841936717ae427ace63c28d04918569a841936717ae427ace63c28d0' d.domain_statuses.count.should == 2 @@ -1298,11 +1305,11 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2302' - response[:results][0][:msg].should == 'Nameserver already exists on this domain' + response[:results][0][:msg].should == 'Nameserver already exists on this domain [hostname]' response[:results][0][:value].should == n.hostname response[:results][1][:result_code].should == '2302' - response[:results][1][:msg].should == 'Contact already exists on this domain' + response[:results][1][:msg].should == 'Contact already exists on this domain [contact_code_cache]' response[:results][1][:value].should == c.code end @@ -1315,7 +1322,8 @@ describe 'EPP Domain', epp: true do } response = epp_plain_request(domain_update_xml(xml_params), :xml) - response[:results][0][:msg].should == 'Required parameter missing: extension > extdata > legalDocument' + response[:results][0][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' response[:results][0][:result_code].should == '2003' end @@ -1372,7 +1380,7 @@ describe 'EPP Domain', epp: true do response = epp_plain_request(xml, :xml) response[:results][0][:result_code].should == '2004' - response[:results][0][:msg].should == 'Period must add up to 1, 2 or 3 years' + response[:results][0][:msg].should == 'Period must add up to 1, 2 or 3 years [period]' response[:results][0][:value].should == '4' end @@ -1524,7 +1532,8 @@ describe 'EPP Domain', epp: true do it 'does not delete domain without legal document' do response = epp_plain_request(@epp_xml.domain.delete(name: { value: 'example.ee' }), :xml) response[:result_code].should == '2003' - response[:msg].should == 'Required parameter missing: extension > extdata > legalDocument' + response[:msg].should == + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' end ### CHECK ### diff --git a/spec/epp/keyrelay_spec.rb b/spec/epp/keyrelay_spec.rb index 7d513391b..731873d08 100644 --- a/spec/epp/keyrelay_spec.rb +++ b/spec/epp/keyrelay_spec.rb @@ -64,7 +64,7 @@ describe 'EPP Keyrelay', epp: true do }) response = epp_plain_request(xml, :xml) - response[:msg].should == 'Required parameter missing: keyrelay > keyData > flags' + response[:msg].should == 'Required parameter missing: keyrelay > keyData > flags [flags]' @registrar2.messages.queued.count.should == msg_count end From 97e54424ecfc1687411b18ebc4a22836439e34f4 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 11:58:02 +0200 Subject: [PATCH 92/97] Add todo to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b7ebd630e..b42af1d24 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ config/secrets.yml config/database.yml /export /ca +todo ## Environment normalisation: /.bundle From 8df1b60fbf17683cb6a9a9dc749d0440f970968e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 12:26:41 +0200 Subject: [PATCH 93/97] Add openssl conf path --- app/models/certificate.rb | 6 +++--- config/application-example.yml | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/certificate.rb b/app/models/certificate.rb index 51d4ac5e4..072d98076 100644 --- a/app/models/certificate.rb +++ b/app/models/certificate.rb @@ -45,7 +45,7 @@ class Certificate < ActiveRecord::Base csr_file.rewind crt_file = Tempfile.new('client_crt') - _out, err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + _out, err, _st = Open3.capture3("openssl ca -config #{ENV['openssl_config_path']} -keyfile #{ENV['ca_key_path']} \ -cert #{ENV['ca_cert_path']} \ -extensions usr_cert -notext -md sha256 \ -in #{csr_file.path} -out #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch") @@ -67,7 +67,7 @@ class Certificate < ActiveRecord::Base crt_file.write(crt) crt_file.rewind - _out, err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + _out, err, _st = Open3.capture3("openssl ca -config #{ENV['openssl_config_path']} -keyfile #{ENV['ca_key_path']} \ -cert #{ENV['ca_cert_path']} \ -revoke #{crt_file.path} -key '#{ENV['ca_key_password']}' -batch") @@ -81,7 +81,7 @@ class Certificate < ActiveRecord::Base return false end - _out, _err, _st = Open3.capture3("openssl ca -keyfile #{ENV['ca_key_path']} \ + _out, _err, _st = Open3.capture3("openssl ca -config #{ENV['openssl_config_path']} -keyfile #{ENV['ca_key_path']} \ -cert #{ENV['ca_cert_path']} \ -gencrl -out #{ENV['crl_path']} -key '#{ENV['ca_key_password']}' -batch") end diff --git a/config/application-example.yml b/config/application-example.yml index 02d1cc7e2..31e5771fc 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -10,6 +10,7 @@ secret_key_base: please-change-it-you-can-generate-it-with-rake-secret devise_secret: please-change-it-you-can-generate-it-with-rake-secret # Used by admin server, you can leave those empty for when running EPP server: +openssl_config_path: '/etc/ssl/openssl.cnf' crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' From 2dc5fc841043741be896bc21aace6e30cfd0e788 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 12:33:47 +0200 Subject: [PATCH 94/97] Update certificate readme --- doc/certificates.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/certificates.md b/doc/certificates.md index ea28a22f6..c8a304f9f 100644 --- a/doc/certificates.md +++ b/doc/certificates.md @@ -88,7 +88,8 @@ Create certificate revocation list (prompts for pass phrase): openssl ca -keyfile private/ca.key.pem -cert certs/ca.crt.pem -gencrl -out crl/crl.pem Configure registry registry/shared/config/application.yml to match the CA settings: - + + openssl_config_path: '/etc/ssl/openssl.cnf' crl_path: '/home/registry/registry/shared/ca/crl/crl.pem' ca_cert_path: '/home/registry/registry/shared/ca/certs/ca.crt.pem' ca_key_path: '/home/registry/registry/shared/ca/private/ca.key.pem' From 3d6e04a9de3505c3bbf82ac13215b36724657501 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 12:52:36 +0200 Subject: [PATCH 95/97] Migration fix --- db/migrate/20150303151224_data_update_regisntrar_codes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20150303151224_data_update_regisntrar_codes.rb b/db/migrate/20150303151224_data_update_regisntrar_codes.rb index 40d2b31cd..e49af65cb 100644 --- a/db/migrate/20150303151224_data_update_regisntrar_codes.rb +++ b/db/migrate/20150303151224_data_update_regisntrar_codes.rb @@ -5,7 +5,7 @@ class DataUpdateRegisntrarCodes < ActiveRecord::Migration next if r.code.present? r[:code] = r.name.parameterize puts "#{r.id}: #{r.changes.inspect}" - r.save! + r.save(validate: false) end end end From 1d28a414aaf379377428b64ae42c51080b354dff Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 15:35:41 +0200 Subject: [PATCH 96/97] Add zonefile settings to seeds --- db/seeds.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/db/seeds.rb b/db/seeds.rb index 15d20166b..975dfa9b3 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -61,4 +61,26 @@ AdminUser.where( country_code: 'EE' ).first_or_create! +ZonefileSetting.where({ + origin: 'ee', + ttl: 43200, + refresh: 3600, + retry: 900, + expire: 1209600, + minimum_ttl: 3600, + email: 'hostmaster.eestiinternet.ee', + master_nameserver: 'ns.tld.ee' +}).first_or_create! + +ZonefileSetting.where({ + origin: 'pri.ee', + ttl: 43200, + refresh: 3600, + retry: 900, + expire: 1209600, + minimum_ttl: 3600, + email: 'hostmaster.eestiinternet.ee', + master_nameserver: 'ns.tld.ee' +}).first_or_create! + AdminUser.update_all(roles: ['admin']) From 81db8e53d7b4e2c20a92b012a008b1bacc188d6e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Thu, 5 Mar 2015 16:40:17 +0200 Subject: [PATCH 97/97] Do not alter public key while generating digest --- app/models/dnskey.rb | 4 ++-- spec/models/dnskey_spec.rb | 32 +++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/app/models/dnskey.rb b/app/models/dnskey.rb index bb194959a..fbdeb80ec 100644 --- a/app/models/dnskey.rb +++ b/app/models/dnskey.rb @@ -85,9 +85,9 @@ class Dnskey < ActiveRecord::Base end def generate_ds_key_tag - public_key.gsub!(' ', '') + pk = public_key.gsub(' ', '') wire_format = [flags, protocol, alg].pack('S!>CC') - wire_format += Base64.decode64(public_key) + wire_format += Base64.decode64(pk) c = 0 wire_format.each_byte.with_index do |b, i| diff --git a/spec/models/dnskey_spec.rb b/spec/models/dnskey_spec.rb index 7b3302cab..dff7d84b2 100644 --- a/spec/models/dnskey_spec.rb +++ b/spec/models/dnskey_spec.rb @@ -61,22 +61,28 @@ describe Dnskey do d = Fabricate(:domain, name: 'emta.ee', dnskeys: [@dnskey]) dk = d.dnskeys.last - dk.public_key = 'AwEAAfB9jK8rj/FAdE3t9bYXiTLpelwlgUyxbHEtvMvhdxs+yHv0h9fE '\ - '710u94LPAeVmXumT6SZPsoo+ALKdmTexkcU9DGQvb2+sPfModBKM/num '\ - 'rScUw1FBe3HwRa9SqQpgpnCjIt0kEVKHAQdLOP86YznSA9uHAg9TTJuT '\ - 'LkUtgtmwNAVFr6/mG+smE1v5NbxPccsFwVTA/T1IyaI4Z48VGCP2WNro '\ - 'R7P6vet1gWhssirnnVYnur8DwWuMJ89o/HjzXeiEGUB8k5SOX+//67FN '\ - 'm8Zs+1ObuAfY8xAHe0L5bxluEbh1T1ARp41QX77EMKVbkcSj7nuBeY8H '\ - 'KiN8HsTvmZyDbRAQQaAJi68qOXsUIoQcpn89PoNoc60F7WlueA6ExSGX '\ - 'KMWIH6nfLXFgidoZ6HxteyUUnZbHEdULjpAoCRuUDjjUnUgFS7eRANfw '\ - 'RCcu9aLziMDp4UU61zVjtmQ7xn3G2W2+2ycqn/vEl/yFyBmHZ+7stpoC '\ - 'd6NTZUn4/ellYSm9lx/vaXdPSinARpYMWtU79Hu/VRifaCQjYkBGAMwK '\ - 'DshX4yJPjza/bqo0XV4WHj1szDFHe0tLN7g1Ojwtf5FR0zyHU3FN9uUa '\ - 'y8a+dowd/fqOQA1jXR04g2PIfFYe0VudCEpmxSV9YDoqjghHeIKUX7Jn '\ - 'KiHL5gk404S5a/Bv' + + pk = 'AwEAAfB9jK8rj/FAdE3t9bYXiTLpelwlgUyxbHEtvMvhdxs+yHv0h9fE '\ + '710u94LPAeVmXumT6SZPsoo+ALKdmTexkcU9DGQvb2+sPfModBKM/num '\ + 'rScUw1FBe3HwRa9SqQpgpnCjIt0kEVKHAQdLOP86YznSA9uHAg9TTJuT '\ + 'LkUtgtmwNAVFr6/mG+smE1v5NbxPccsFwVTA/T1IyaI4Z48VGCP2WNro '\ + 'R7P6vet1gWhssirnnVYnur8DwWuMJ89o/HjzXeiEGUB8k5SOX+//67FN '\ + 'm8Zs+1ObuAfY8xAHe0L5bxluEbh1T1ARp41QX77EMKVbkcSj7nuBeY8H '\ + 'KiN8HsTvmZyDbRAQQaAJi68qOXsUIoQcpn89PoNoc60F7WlueA6ExSGX '\ + 'KMWIH6nfLXFgidoZ6HxteyUUnZbHEdULjpAoCRuUDjjUnUgFS7eRANfw '\ + 'RCcu9aLziMDp4UU61zVjtmQ7xn3G2W2+2ycqn/vEl/yFyBmHZ+7stpoC '\ + 'd6NTZUn4/ellYSm9lx/vaXdPSinARpYMWtU79Hu/VRifaCQjYkBGAMwK '\ + 'DshX4yJPjza/bqo0XV4WHj1szDFHe0tLN7g1Ojwtf5FR0zyHU3FN9uUa '\ + 'y8a+dowd/fqOQA1jXR04g2PIfFYe0VudCEpmxSV9YDoqjghHeIKUX7Jn '\ + 'KiHL5gk404S5a/Bv' + + dk.public_key = pk + dk.save dk.ds_digest.should == 'D7045D3C2EF7332409A132D935C8E2834A2AAB769B35BC370FA68C9445398288' dk.ds_key_tag.should == '31051' + + dk.public_key.should == pk end end end