diff --git a/.gitignore b/.gitignore index 7f705cb07..175f485fc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,11 @@ capybara-*.html /spec/tmp **.orig config/initializers/secret_token.rb -config/application.yml config/secrets.yml config/database.yml /export +/ca +todo ## Environment normalisation: /.bundle @@ -24,3 +25,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/.rubocop.yml b/.rubocop.yml index c58f51bdb..ed6970b34 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -77,3 +77,7 @@ Style/AsciiComments: # because NerdCommenter honors commented code intentions Style/CommentIndentation: Enabled: false + +# It did not alayws suggested good format +Style/AlignParameters: + Enabled: false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..5fc709227 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +language: ruby +rvm: + - 2.2 + - ruby-head +env: + - DB=postgresql +sudo: false +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/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 +script: + - RAILS_ENV=test bundle exec rake +cache: bundler +services: + - postgresql +addons: + postgresql: "9.3" diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f0cc390..cc0bd3e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,93 +1,11 @@ -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 -``` +27.02.2015 -Generate the root key (prompts for pass phrase): -``` -openssl genrsa -aes256 -out private/ca.key.pem 4096 -``` +* Simplified config/application-example.yml, + now system will check if all required settings are present in application.yml -Configure OpenSSL: -``` -sudo su - -cd /etc/ssl/ -cp openssl.cnf openssl.cnf.bak -nano openssl.cnf -exit -``` +19.02.2015 -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' -``` +* Cetrificate only enabled, please setup certificates following doc/certificate.md document. 20.01.2015 diff --git a/Gemfile b/Gemfile index c901c0e56..e0bf28b53 100644 --- a/Gemfile +++ b/Gemfile @@ -5,10 +5,16 @@ 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 -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 @@ -110,4 +116,7 @@ group :development, :test do # dev tools gem 'unicorn' + + # for travis + gem 'rake' end diff --git a/Gemfile.lock b/Gemfile.lock index 9097efcef..478d7604a 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 @@ -142,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) @@ -235,9 +247,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 +318,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) @@ -451,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) @@ -469,7 +480,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) @@ -478,6 +489,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/Guardfile b/Guardfile index eab74ee21..e6d0a443a 100644 --- a/Guardfile +++ b/Guardfile @@ -3,11 +3,11 @@ group :red_green_refactor, halt_on_fail: true do # be sure you have apache2 configured to # accept EPP request on port 701, what proxy to 8989. # port and environment is just for correct notification, all is overwritten by CLI - guard :rails, port: 8989, environment: 'test' do - # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do - watch('Gemfile.lock') - watch(%r{^(config|lib)/.*}) - end + # guard :rails, port: 8989, environment: 'test' do + # # guard :rails, port: 8989, environment: 'test', CLI: 'RAILS_ENV=test unicorn -p 8989' do + # watch('Gemfile.lock') + # watch(%r{^(config|lib)/.*}) + # end guard :rspec, cmd: 'spring rspec', notification: false do watch(%r{^spec/.+_spec\.rb$}) @@ -18,6 +18,8 @@ group :red_green_refactor, halt_on_fail: true do watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^app/controllers/epp/(.+)_(controller)\.rb$}) { |m| ["spec/epp/#{m[1].sub(/s$/,'')}_spec.rb"] } + watch(%r{^app/models/epp/(.+)\.rb$}) { |m| "spec/epp/#{m[1]}_spec.rb" } watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch('config/routes.rb') { "spec/routing" } watch('app/controllers/application_controller.rb') { "spec/controllers" } diff --git a/README.md b/README.md index 92b8f67c6..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,44 +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 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 - -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 @@ -152,7 +119,12 @@ 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 + 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" EPPEngine On EPPCommandRoot /proxy/command @@ -182,6 +154,21 @@ 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: @@ -196,85 +183,7 @@ Please follow WHOIS server readme: https://github.com/internetee/whois -Deployment ----------- +## Code Status -* [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 +Alpha release status, only model tests: +[![Build Status](https://travis-ci.org/domify/registry.svg?branch=master)](https://travis-ci.org/domify/registry) diff --git a/app/controllers/admin/api_users_controller.rb b/app/controllers/admin/api_users_controller.rb index 54105e87e..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 @@ -53,14 +47,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 @@ -68,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/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/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 9810f7bdf..8bea17fdf 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,4 +1,3 @@ class AdminController < ApplicationController before_action :authenticate_user! - check_authorization end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 81acf1313..8840e4989 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,6 @@ class ApplicationController < ActionController::Base + check_authorization unless: :devise_controller? + # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception @@ -9,8 +11,14 @@ class ApplicationController < ActionController::Base params[resource] &&= send(method) if respond_to?(method, true) end + rescue_from CanCan::AccessDenied do |exception| + redirect_to admin_dashboard_path, alert: exception.message + end + def after_sign_in_path_for(_resource) - return session[:user_return_to].to_s if session[:user_return_to] && session[:user_return_to] != login_path + if session[:user_return_to] && session[:user_return_to] != login_path + return session[:user_return_to].to_s + end admin_dashboard_path end @@ -34,9 +42,3 @@ class ApplicationController < ActionController::Base end end end - -class ApplicationController < ActionController::Base - rescue_from CanCan::AccessDenied do |exception| - redirect_to admin_dashboard_path, alert: exception.message - end -end diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 6aa5efce3..d93916055 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,188 +1,125 @@ class Epp::ContactsController < EppController - helper WhodunnitHelper ## Refactor this? - - def create - @contact = Contact.new(contact_and_address_attributes) - @contact.registrar = current_user.registrar - render_epp_response '/epp/contacts/create' and return if @contact.save - handle_errors(@contact) - end - - def update - # FIXME: Update returns 2303 update multiple times - code = params_hash['epp']['command']['update']['update'][:id] - - @contact = Contact.where(code: code).first - # if update_rights? && @contact.update_attributes(contact_and_address_attributes(:update)) - if owner? && @contact.update_attributes(contact_and_address_attributes(:update)) - render_epp_response 'epp/contacts/update' - else - contact_exists?(code) - handle_errors(@contact) and return - end - end - - # rubocop:disable Metrics/CyclomaticComplexity - def delete - @contact = find_contact - handle_errors(@contact) and return unless rights? # owner? - handle_errors(@contact) and return unless @contact - handle_errors(@contact) and return unless @contact.destroy_and_clean - - render_epp_response '/epp/contacts/delete' - end - # rubocop:enable Metrics/CyclomaticComplexity - - def check - ph = params_hash['epp']['command']['check']['check'] - @contacts = Contact.check_availability(ph[:id]) - render_epp_response '/epp/contacts/check' - end + before_action :find_contact, only: [:info, :update, :delete] + before_action :find_password, only: [:info, :update, :delete] def info - handle_errors(@contact) and return unless @contact && rights? - # handle_errors(@contact) and return unless rights? - @disclosure = ContactDisclosure.default_values.merge(@contact.disclosure.try(:as_hash) || {}) - @disclosure_policy = @contact.disclosure.try(:attributes_with_flag) - @owner = owner?(false) - # need to reload contact eagerly - @contact = find_contact if @owner # for clarity, could just be true + authorize! :info, @contact, @password render_epp_response 'epp/contacts/info' end + def check + authorize! :check, Epp::Contact + + ids = params[:parsed_frame].css('id').map(&:text) + @results = Contact.check_availability(ids) + render_epp_response '/epp/contacts/check' + end + + def create + authorize! :create, Epp::Contact + @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) + + if @contact.save + render_epp_response '/epp/contacts/create' + else + handle_errors(@contact) + end + end + + def update + authorize! :update, @contact, @password + + if @contact.update_attributes(params[:parsed_frame]) + render_epp_response 'epp/contacts/update' + else + handle_errors(@contact) + end + end + + def delete + authorize! :delete, @contact, @password + + if @contact.destroy_and_clean + render_epp_response '/epp/contacts/delete' + else + handle_errors(@contact) + end + end + def renew + authorize! :renew, Epp::Contact epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') } handle_errors end - ## HELPER METHODS - private - ## CREATE - def validate_create - @ph = params_hash['epp']['command']['create']['create'] - return false unless validate_params - xml_attrs_present?(@ph, [%w(postalInfo name), %w(postalInfo addr city), %w(postalInfo addr cc), - %w(ident), %w(voice), %w(email)]) - - epp_errors.empty? + def find_password + @password = params[:parsed_frame].css('authInfo pw').text end - ## UPDATE - def validate_updatezz - @ph = params_hash['epp']['command']['update']['update'] - update_attrs_present? - # xml_attrs_present?(@ph, [['id'], %w(authInfo pw)]) - xml_attrs_present?(@ph, [['id']]) - end - - def contact_exists?(code) - return true if @contact.is_a?(Contact) - epp_errors << { code: '2303', msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: code } } - end - - def update_attrs_present? - return true if params[:parsed_frame].css('add').present? - return true if params[:parsed_frame].css('rem').present? - return true if params[:parsed_frame].css('chg').present? - epp_errors << { code: '2003', msg: I18n.t('errors.messages.required_parameter_missing', key: 'add, rem or chg') } - end - - ## DELETE - def validate_delete - @ph = params_hash['epp']['command']['delete']['delete'] - xml_attrs_present?(@ph, [['id']]) - end - - ## check - def validate_check - @ph = params_hash['epp']['command']['check']['check'] - xml_attrs_present?(@ph, [['id']]) - end - - ## info - def validate_info # and process - @ph = params_hash['epp']['command']['info']['info'] - return false unless xml_attrs_present?(@ph, [['id']]) - @contact = find_contact - return false unless @contact - return true if current_user.registrar == @contact.registrar || xml_attrs_present?(@ph, [%w(authInfo pw)]) - false - end - - ## SHARED - def find_contact - contact = Contact.find_by(code: @ph[:id]) - unless contact - epp_errors << { code: '2303', - msg: t('errors.messages.epp_obj_does_not_exist'), - value: { obj: 'id', val: @ph[:id] } } + code = params[:parsed_frame].css('id').text.strip.downcase + @contact = Epp::Contact.find_by(code: code) + + if @contact.blank? + epp_errors << { + code: '2303', + msg: t('errors.messages.epp_obj_does_not_exist'), + value: { obj: 'id', val: code } + } + fail CanCan::AccessDenied end - contact + @contact end - def owner?(with_errors = true) - return false unless find_contact - return true if @contact.registrar == current_user.registrar - return false unless with_errors - epp_errors << { code: '2201', msg: t('errors.messages.epp_authorization_error') } - false + # + # Validations + # + def validate_info + @prefix = 'info > info >' + requires 'id' end - def rights? - pw = @ph.try(:[], :authInfo).try(:[], :pw) - - return true if current_user.try(:registrar) == @contact.try(:registrar) - return true if pw && @contact.auth_info_matches(pw) # @contact.try(:auth_info_matches, pw) - - epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') } - false + def validate_check + @prefix = 'check > check >' + requires 'id' end - def update_rights? - pw = @ph.try(:[], :authInfo).try(:[], :pw) - return true if pw && @contact.auth_info_matches(pw) - epp_errors << { code: '2200', msg: t('errors.messages.epp_authentication_error') } - false - end - - def contact_and_address_attributes(type = :create) - case type - when :update - # TODO: support for rem/add - contact_hash = merge_attribute_hash(@ph[:chg], type).delete_if { |_k, v| v.empty? } - else - contact_hash = merge_attribute_hash(@ph, type) - end - contact_hash[:ident_type] = ident_type unless ident_type.nil? - contact_hash - end - - def merge_attribute_hash(prms, type) - contact_hash = Contact.extract_attributes(prms, type) - contact_hash = contact_hash.merge( - Address.extract_attributes((prms.try(:[], :postalInfo) || [])) + def validate_create + @prefix = 'create > create >' + requires( + 'postalInfo > name', 'postalInfo > addr > city', + 'postalInfo > addr > cc', 'ident', 'voice', 'email' ) - contact_hash[:disclosure_attributes] = - ContactDisclosure.extract_attributes(params[:parsed_frame]) - - contact_hash + ident = params[:parsed_frame].css('ident') + if ident.present? && ident.text != 'birthday' && ident.attr('cc').blank? + epp_errors << { + code: '2003', + msg: I18n.t('errors.messages.required_attribute_missing', key: 'ident country code missing') + } + end + @prefix = nil + requires 'extension > extdata > legalDocument' end - def ident_type - result = params[:parsed_frame].css('ident').first.try(:attributes).try(:[], 'type').try(:value) - return nil unless result - - Contact::IDENT_TYPES.any? { |type| return type if result.include?(type) } - nil + def validate_update + @prefix = 'update > update >' + if element_count('chg') == 0 && element_count('rem') == 0 && element_count('add') == 0 + epp_errors << { + code: '2003', + msg: I18n.t('errors.messages.required_parameter_missing', key: 'add, rem or chg') + } + end + requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end - def validate_params - return true if @ph - epp_errors << { code: '2001', msg: t(:'errors.messages.epp_command_syntax_error') } - false + def validate_delete + @prefix = 'delete > delete >' + requires 'id', 'authInfo > pw' + @prefix = nil + requires 'extension > extdata > legalDocument' end end diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 2a4411ba0..97aa0967f 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) # @domain.parse_and_attach_domain_dependencies(params[:parsed_frame]) @@ -334,7 +336,7 @@ class Epp::DomainsController < EppController return domain if domain.auth_info == params[:parsed_frame].css('authInfo pw').text - if (domain.registrar != current_user.registrar && secure[:secure] == true) && + if (domain.registrar != current_user.registrar) && secure[:secure] == true epp_errors << { code: '2302', msg: I18n.t('errors.messages.domain_exists_but_belongs_to_other_registrar'), diff --git a/app/controllers/epp/errors_controller.rb b/app/controllers/epp/errors_controller.rb index 43b8ee6b1..cefa026a0 100644 --- a/app/controllers/epp/errors_controller.rb +++ b/app/controllers/epp/errors_controller.rb @@ -1,4 +1,6 @@ class Epp::ErrorsController < EppController + skip_authorization_check # TODO: remove it + def error epp_errors << { code: params[:code], msg: params[:msg] } render_epp_response '/epp/error' diff --git a/app/controllers/epp/keyrelays_controller.rb b/app/controllers/epp/keyrelays_controller.rb index f84694e5b..8a9b863d4 100644 --- a/app/controllers/epp/keyrelays_controller.rb +++ b/app/controllers/epp/keyrelays_controller.rb @@ -1,4 +1,6 @@ class Epp::KeyrelaysController < EppController + skip_authorization_check # TODO: remove it + # rubocop: disable Metrics/PerceivedComplexity # rubocop: disable Metrics/CyclomaticComplexity def keyrelay diff --git a/app/controllers/epp/polls_controller.rb b/app/controllers/epp/polls_controller.rb index 3376956e2..2f445abc6 100644 --- a/app/controllers/epp/polls_controller.rb +++ b/app/controllers/epp/polls_controller.rb @@ -1,4 +1,6 @@ class Epp::PollsController < EppController + skip_authorization_check # TODO: remove it + def poll req_poll if params[:parsed_frame].css('poll').first['op'] == 'req' ack_poll if params[:parsed_frame].css('poll').first['op'] == 'ack' @@ -38,6 +40,6 @@ class Epp::PollsController < EppController private def validate_poll - requires_attribute 'poll', 'op', values: %(ack req) + requires_attribute 'poll', 'op', values: %(ack req), allow_blank: true end end diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 1485001a8..181cc19ac 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -1,12 +1,24 @@ class Epp::SessionsController < EppController + skip_authorization_check only: [:hello, :login, :logout] + def hello render_epp_response('greeting') end + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity def login - @api_user = ApiUser.find_by(login_params) + cert_valid = true + 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] + 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 @@ -14,6 +26,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 diff --git a/app/controllers/epp_controller.rb b/app/controllers/epp_controller.rb index 28eff040f..a0985be4a 100644 --- a/app/controllers/epp_controller.rb +++ b/app/controllers/epp_controller.rb @@ -1,10 +1,23 @@ class EppController < ApplicationController + layout false protect_from_forgery with: :null_session + skip_before_action :verify_authenticity_token + before_action :generate_svtrid before_action :validate_request - layout false helper_method :current_user + rescue_from CanCan::AccessDenied do |_exception| + @errors ||= [] + if @errors.blank? + @errors = [{ + msg: t('errors.messages.epp_authorization_error'), + code: '2201' + }] + end + render_epp_response '/epp/error' + end + def generate_svtrid # rubocop: disable Style/VariableName @svTRID = "ccReg-#{format('%010d', rand(10**10))}" @@ -84,15 +97,23 @@ 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(' ') + attr = selector.split('>').last.strip.underscore 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) + msg: I18n.t('errors.messages.required_parameter_missing', key: "#{full_selector} [#{attr}]") } if missing end @@ -105,7 +126,7 @@ class EppController < ApplicationController # requires_attribute 'transfer', 'op', values: %(approve, query, reject) def requires_attribute(element_selector, attribute_selector, options) - element = requires(element_selector) + element = requires(element_selector, allow_blank: options[:allow_blank]) return unless element attribute = element[attribute_selector] diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a6c1a9ff6..ff4f74517 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,6 @@ class SessionsController < Devise::SessionsController + skip_authorization_check + def create # TODO: Create ID Card login here: # this is just testing config diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cc61f56db..0c51ce5a7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -8,4 +8,13 @@ module ApplicationHelper return '' if unstable_env.nil? "background-image: url(#{image_path(unstable_env.to_s + '.png')});" end + + def ident_indicator(contact) + case contact.ident_type + when 'birthday' + "[#{contact.ident_type}]" + else + "[#{contact.ident_country_code} #{contact.ident_type}]" + end + end end diff --git a/app/helpers/whodunnit_helper.rb b/app/helpers/whodunnit_helper.rb deleted file mode 100644 index a1d2999c6..000000000 --- a/app/helpers/whodunnit_helper.rb +++ /dev/null @@ -1,25 +0,0 @@ -module WhodunnitHelper - def link_to_whodunnit(whodunnit) - return nil unless whodunnit - if whodunnit.include?('-ApiUser') - user = ApiUser.find(whodunnit) - return link_to(user.username, admin_epp_user_path(user)) - end - user = AdminUser.find(whodunnit) - return link_to(user.username, admin_user_path(user)) - rescue ActiveRecord::RecordNotFound - return nil - end - - def whodunnit_with_protocol(whodunnit) - return nil unless whodunnit - if whodunnit.include?('-ApiUser') - user = ApiUser.find(whodunnit) - return "#{user.username} (EPP)" - end - user = AdminUser.find(whodunnit) - return user.username - rescue ActiveRecord::RecordNotFound - return nil - end -end diff --git a/app/models/ability.rb b/app/models/ability.rb index 47fc6c209..bb47bb2a8 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,16 +2,31 @@ class Ability include CanCan::Ability def initialize(user) - alias_action :create, :read, :update, :destroy, to: :crud + alias_action :show, :create, :update, :destroy, to: :crud @user = user || AdminUser.new - @user.roles.each { |role| send(role) } if @user.roles - return if @user.roles || @user.roles.any? + case @user.class.to_s + when 'AdminUser' + @user.roles.each { |role| send(role) } if @user.roles + when 'ApiUser' + epp + end can :show, :dashboard end + def epp + # Epp::Contact + can(:info, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw } + can(:check, Epp::Contact) + can(:create, Epp::Contact) + can(:update, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw } + can(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw } + can(:renew, Epp::Contact) + can(:view_password, Epp::Contact) { |c| c.registrar_id == @user.registrar_id } + end + def user can :show, :dashboard end @@ -30,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 fe0368125..d95ce8414 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -5,14 +5,24 @@ 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 - before_save :create_crt, if: -> (au) { au.csr_changed? } - attr_accessor :registrar_typeahead + def ability + @ability ||= Ability.new(self) + end + delegate :can?, :cannot?, to: :ability + + after_initialize :set_defaults + def set_defaults + return unless new_record? + self.active = true unless active_changed? + end + def registrar_typeahead @registrar_typeahead || registrar || nil end @@ -24,28 +34,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/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 new file mode 100644 index 000000000..072d98076 --- /dev/null +++ b/app/models/certificate.rb @@ -0,0 +1,88 @@ +class Certificate < ActiveRecord::Base + include Versions + + belongs_to :api_user + + 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(ENV['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 -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") + + 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 -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") + + 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') + logger.error(err) + return false + end + + _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 +end diff --git a/app/models/concerns/epp_errors.rb b/app/models/concerns/epp_errors.rb index 4d95cea27..cf3824260 100644 --- a/app/models/concerns/epp_errors.rb +++ b/app/models/concerns/epp_errors.rb @@ -3,42 +3,43 @@ 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 } + msg = attr.to_sym == :base ? err : "#{err} [#{attr}]" + epp_errors << { code: code, msg: msg, 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) 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..da8a3f651 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -1,6 +1,5 @@ class Contact < ActiveRecord::Base include Versions # version/contact_version.rb - include EppErrors has_one :address, dependent: :destroy has_one :disclosure, class_name: 'ContactDisclosure', dependent: :destroy @@ -8,45 +7,46 @@ class Contact < ActiveRecord::Base has_many :domain_contacts has_many :domains, through: :domain_contacts has_many :statuses, class_name: 'ContactStatus' + has_many :legal_documents, as: :documentable belongs_to :registrar - accepts_nested_attributes_for :address, :disclosure + accepts_nested_attributes_for :address, :disclosure, :legal_documents validates :name, :phone, :email, :ident, :address, :registrar, :ident_type, presence: true # Phone nr validation is very minimam in order to support legacy requirements validates :phone, format: /\+[0-9]{1,3}\.[0-9]{1,14}?/ validates :email, format: /@/ - validates :ident, format: /\d{4}-\d{2}-\d{2}/, if: proc { |c| c.ident_type == 'birthday' } + 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 }, + format: { with: /\A[\w\-\:]*\Z/i }, + length: { maximum: 100 } - validate :ident_must_be_valid + validate :ident_valid_format? - validates :code, uniqueness: { message: :epp_id_taken } + 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 - delegate :city, to: :address # , prefix: true - delegate :street, to: :address # , prefix: true - delegate :zip, to: :address # , prefix: true - - # callbacks - # TODO: remove old - # after_commit :domains_snapshot - # after_update :domains_snapshot - # after_destroy :domains_snapshot + before_validation :set_ident_country_code before_create :generate_code before_create :generate_auth_info after_create :ensure_disclosure - # scopes scope :current_registrars, ->(id) { where(registrar_id: id) } - IDENT_TYPE_ICO = 'ico' + IDENT_TYPE_BIC = 'bic' IDENT_TYPES = [ - IDENT_TYPE_ICO, # Company registry code (or similar) - 'bic', # Business registry code + IDENT_TYPE_BIC, # Company registry code (or similar) 'priv', # National idendtification number - 'op', # Estonian ID, depricated - 'passport', # Passport number, depricated 'birthday' # Birthday date ] @@ -54,124 +54,10 @@ class Contact < ActiveRecord::Base CONTACT_TYPE_ADMIN = 'admin' CONTACT_TYPES = [CONTACT_TYPE_TECH, CONTACT_TYPE_ADMIN] - def ident_must_be_valid - # TODO: Ident can also be passport number or company registry code. - # so have to make changes to validations (and doc/schema) accordingly - return true unless ident.present? && ident_type.present? && ident_type == 'op' - code = Isikukood.new(ident) - errors.add(:ident, 'bad format') unless code.valid? - end - - def ensure_disclosure - create_disclosure! unless disclosure - end - - # TODO: remove old - # def domains_snapshot - # (domains + domains_owned).uniq.each do |domain| - # next unless domain.is_a?(Domain) - # # next if domain.versions.last == domain.create_snapshot - # domain.create_version # Method from paper_trail - # end - # end - - def juridical? - ident_type == IDENT_TYPE_ICO - end - - def citizen? - ident_type != IDENT_TYPE_ICO - end - - def cr_id - # created_by ? created_by.username : nil - end - - def up_id - # updated_by ? updated_by.username : nil - end - - def auth_info_matches(pw) - auth_info == pw - end - - # generate random id for contact - def generate_code - self.code = SecureRandom.hex(4) - end - - def generate_auth_info - self.auth_info = SecureRandom.hex(16) - end - - # Find a way to use self.domains with contact - def domains_owned - Domain.where(owner_contact_id: id) - end - - def relations_with_domain? - return true if domain_contacts.present? || domains_owned.present? - false - end - - # should use only in transaction - def destroy_and_clean - if relations_with_domain? - errors.add(:domains, :exist) - return false - 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? } + 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) @@ -188,10 +74,84 @@ class Contact < ActiveRecord::Base res end + end - def search_by_query(query) - res = search(code_cont: query).result - res.reduce([]) { |o, v| o << { id: v[:id], display_key: "#{v.name} (#{v.code})" } } + def to_s + name + end + + def ident_valid_format? + case ident_type + when 'priv' + case ident_country_code + when 'EE' + code = Isikukood.new(ident) + errors.add(:ident, :invalid_EE_identity_format) unless code.valid? + end + end + end + + def ensure_disclosure + create_disclosure! unless disclosure + end + + def bic? + ident_type == IDENT_TYPE_BIC + end + + def priv? + ident_type != IDENT_TYPE_BIC + end + + def generate_code + 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) + end + + def relations_with_domain? + return true if domain_contacts.present? || domains_owned.present? + 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? + errors.add(:domains, :exist) + return false + 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/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/app/models/domain.rb b/app/models/domain.rb index 3ca9e0f51..b6a0b6faa 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, @@ -43,11 +41,11 @@ class Domain < ActiveRecord::Base has_many :legal_documents, as: :documentable accepts_nested_attributes_for :legal_documents, reject_if: proc { |attrs| attrs[:body].blank? } - 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 @@ -118,11 +116,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 @@ -131,52 +124,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! @@ -265,7 +212,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 @@ -305,36 +252,36 @@ class Domain < ActiveRecord::Base # rubocop:disable Metrics/MethodLength def update_whois_body self.whois_body = <<-EOS - This Whois Server contains information on - Estonian Top Level Domain ee TLD + This Whois Server contains information on + Estonian Top Level Domain ee TLD - domain: #{name} - registrar: #{registrar} - status: - registered: - changed: #{updated_at.to_s(:db)} - expire: - outzone: - delete: + domain: #{name} + registrar: #{registrar} + status: + registered: + changed: #{updated_at.to_s(:db)} + expire: + outzone: + delete: - contact - name: - e-mail: - registrar: - created: + contact + name: + e-mail: + registrar: + created: - contact: + contact: - nsset: - nserver: + nsset: + nserver: - registrar: - org: - url: - phone: - address: - created: - changed: + registrar: + org: + url: + phone: + address: + created: + changed: EOS end # rubocop:enabled Metrics/MethodLength diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb new file mode 100644 index 000000000..d9df7f2ae --- /dev/null +++ b/app/models/epp/contact.rb @@ -0,0 +1,102 @@ +# 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 + # rubocop: disable Metrics/MethodLength + def attrs_from(frame) + f = frame + at = {}.with_indifferent_access + at[:name] = f.css('postalInfo name').text if f.css('postalInfo name').present? + at[:org_name] = f.css('postalInfo org').text if f.css('postalInfo org').present? + at[:email] = f.css('email').text if f.css('email').present? + at[:fax] = f.css('fax').text if f.css('fax').present? + at[:phone] = f.css('voice').text if f.css('voice').present? + at[:auth_info] = f.css('authInfo pw').text if f.css('authInfo pw').present? + + if f.css('ident').present? && f.css('ident').attr('type').present? + at[:ident] = f.css('ident').text + at[:ident_type] = f.css('ident').attr('type').try(:text) + at[:ident_country_code] = f.css('ident').attr('cc').try(:text) + end + + at[:address_attributes] = {}.with_indifferent_access + sat = at[:address_attributes] + sat[:city] = f.css('postalInfo addr city').text if f.css('postalInfo addr city').present? + sat[:zip] = f.css('postalInfo addr pc').text if f.css('postalInfo addr pc').present? + sat[:street] = f.css('postalInfo addr street').text if f.css('postalInfo addr street').present? + sat[:state] = f.css('postalInfo addr sp').text if f.css('postalInfo addr sp').present? + sat[:country_code] = f.css('postalInfo addr cc').text if f.css('postalInfo addr cc').present? + at.delete(:address_attributes) if at[:address_attributes].blank? + + legal_frame = f.css('legalDocument').first + if legal_frame.present? + at[:legal_documents_attributes] = legal_document_attrs(legal_frame) + end + + at + end + # rubocop: enable Metrics/MethodLength + # rubocop: enable Metrics/PerceivedComplexity + # rubocop: enable Metrics/CyclomaticComplexity + + def new(frame, registrar) + return super if frame.blank? + + 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) + [{ + body: legal_frame.text, + document_type: legal_frame['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], + [:ident, :invalid_EE_identity_format], + [:ident, :invalid_birthday_format] + ] + } + end + + def update_attributes(frame) + return super if frame.blank? + at = {}.with_indifferent_access + at.deep_merge!(self.class.attrs_from(frame.css('chg'))) + legal_frame = frame.css('legalDocument').first + at[:legal_documents_attributes] = self.class.legal_document_attrs(legal_frame) + + super(at) + end +end +# rubocop: enable Metrics/ClassLength diff --git a/app/models/epp/epp_domain.rb b/app/models/epp/epp_domain.rb index 0cd728499..75c348afe 100644 --- a/app/models/epp/epp_domain.rb +++ b/app/models/epp/epp_domain.rb @@ -146,7 +146,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/models/registrar.rb b/app/models/registrar.rb index 219e5d06d..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( @@ -23,7 +31,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 @@ -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/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/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/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 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/app/views/admin/contacts/index.haml b/app/views/admin/contacts/index.haml index 7c27cf646..3f437d62d 100644 --- a/app/views/admin/contacts/index.haml +++ b/app/views/admin/contacts/index.haml @@ -22,20 +22,20 @@ %thead %tr %th{class: 'col-xs-2'} - = sort_link(@q, 'name', t('name')) + = sort_link(@q, 'name', t(:name)) %th{class: 'col-xs-2'} - = sort_link(@q, 'code', t('code')) + = sort_link(@q, 'ident', t(:identity)) %th{class: 'col-xs-2'} - = sort_link(@q, 'ident', t('identity_code')) + = sort_link(@q, 'email', t(:email)) %th{class: 'col-xs-2'} - = sort_link(@q, 'email', t('email')) + = sort_link(@q, 'code', t(:code)) %tbody - - @contacts.each do |x| + - @contacts.each do |contact| %tr - %td= link_to(x, admin_contact_path(x)) - %td= x.code - %td= x.ident - %td= x.email + %td= link_to(contact, admin_contact_path(contact)) + %td= "#{contact.ident} #{ident_indicator(contact)}" + %td= contact.email + %td= contact.code .row .col-md-12 = paginate @contacts diff --git a/app/views/admin/contacts/partials/_address.haml b/app/views/admin/contacts/partials/_address.haml index 1d27ba7bd..aecba9a70 100644 --- a/app/views/admin/contacts/partials/_address.haml +++ b/app/views/admin/contacts/partials/_address.haml @@ -1,28 +1,24 @@ .panel.panel-default .panel-heading - %h3.panel-title= t('address') + %h3.panel-title= t(:address) .panel-body - unless @contact.address.nil? %dl.dl-horizontal - %dt= t('country') - %dd= @contact.address.country + - if @contact.bic? + %dt= t(:org_name) + %dd= @contact.org_name - %dt= t('city') - %dd= @contact.address.city + %dt= t(:street) + %dd= @contact.street - %dt= t('street') - %dd= @contact.address.street + %dt= t(:city) + %dd= @contact.city - - if @contact.address.street2 - %dt= t('street') - %dd= @contact.address.street2 + %dt= t(:zip) + %dd= @contact.zip - - if @contact.address.street3 - %dt= t('street') - %dd= @contact.address.street3 - - - - %dt= t('zip') - %dd= @contact.address.zip + %dt= t(:state) + %dd= @contact.state + %dt= t(:country) + %dd= @contact.country diff --git a/app/views/admin/contacts/partials/_general.haml b/app/views/admin/contacts/partials/_general.haml index d5cc42044..2911ab783 100644 --- a/app/views/admin/contacts/partials/_general.haml +++ b/app/views/admin/contacts/partials/_general.haml @@ -3,30 +3,25 @@ %h3.panel-title= t('general') .panel-body %dl.dl-horizontal - %dt= t('name') - %dd= @contact.name + %dt= t(:ident) + %dd= "#{@contact.ident} #{ident_indicator(@contact)}" - %dt= t('org_name') - %dd= @contact.org_name + %br - %dt= t('code') - %dd= @contact.code - - %dt= t('ident') - %dd= @contact.ident - - %dt= t('ident_type') - %dd= @contact.ident_type - - %dt= t('email') + %dt= t(:email) %dd= @contact.email - %dt= t('phone') + %dt= t(:phone) %dd= @contact.phone - if @contact.fax - %dt= t('fax') + %dt= t(:fax) %dd= @contact.fax + %br + + %dt= t(:code) + %dd= @contact.code + %dt= t('password') %dd= @contact.auth_info diff --git a/app/views/admin/contacts/show.haml b/app/views/admin/contacts/show.haml index d5620bb7c..dba7dd39a 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -1,10 +1,13 @@ .row .col-sm-12 %h2.text-center-xs - = "#{t('contact_details')}" + = @contact.name %hr .row .col-md-6= render 'admin/contacts/partials/general' .col-md-6= render 'admin/contacts/partials/address' .row .col-md-12= render 'admin/contacts/partials/domains' +.row + .col-md-12 + = render 'admin/domains/partials/legal_documents', legal_documents: @contact.legal_documents diff --git a/app/views/admin/domains/partials/_legal_documents.haml b/app/views/admin/domains/partials/_legal_documents.haml index b9fe5144e..5a25ae325 100644 --- a/app/views/admin/domains/partials/_legal_documents.haml +++ b/app/views/admin/domains/partials/_legal_documents.haml @@ -8,7 +8,7 @@ %th{class: 'col-xs-8'}= t('created_at') %th{class: 'col-xs-4'}= t('type') %tbody - - @domain.legal_documents.each do |x| + - legal_documents.each do |x| %tr %td= link_to(x.created_at, [:admin, x]) %td= x.document_type diff --git a/app/views/admin/domains/show.haml b/app/views/admin/domains/show.haml index bbb790737..215ade8b1 100644 --- a/app/views/admin/domains/show.haml +++ b/app/views/admin/domains/show.haml @@ -24,4 +24,5 @@ .row .col-md-12= render 'admin/domains/partials/keyrelays' .row - .col-md-12= render 'admin/domains/partials/legal_documents' + .col-md-12 + = render 'admin/domains/partials/legal_documents', legal_documents: @domain.legal_documents diff --git a/app/views/epp/contacts/_postal_info.xml.builder b/app/views/epp/contacts/_postal_info.xml.builder index f9c6c5ee9..f84f177c9 100644 --- a/app/views/epp/contacts/_postal_info.xml.builder +++ b/app/views/epp/contacts/_postal_info.xml.builder @@ -1,13 +1,13 @@ -address = @contact.address xml.tag!('contact:postalInfo', type: 'int') do - xml.tag!('contact:name', @contact.name) if @disclosure.try(:[], :name) || @owner - xml.tag!('contact:org', @contact.org_name) if @disclosure.try(:[], :org_name) || @owner - if @disclosure.try(:addr) || @owner + xml.tag!('contact:name', @contact.name) #if @disclosure.try(:[], :name) || @owner + xml.tag!('contact:org', @contact.org_name) #if @disclosure.try(:[], :org_name) || @owner + # if @disclosure.try(:addr) || @owner xml.tag!('contact:addr') do - xml.tag!('contact:street', address.street) if address - xml.tag!('contact:cc', address.country_code) unless address.country_code.nil? - xml.tag!('contact:city', address.city) if address + xml.tag!('contact:street', @contact.street) + xml.tag!('contact:city', @contact.city) + xml.tag!('contact:pc', @contact.zip) + xml.tag!('contact:sp', @contact.state) + xml.tag!('contact:cc', @contact.country_code) end - end + # end end - diff --git a/app/views/epp/contacts/check.xml.builder b/app/views/epp/contacts/check.xml.builder index dab344196..4df3597e3 100644 --- a/app/views/epp/contacts/check.xml.builder +++ b/app/views/epp/contacts/check.xml.builder @@ -6,11 +6,10 @@ xml.epp_head do xml.resData do xml.tag!('contact:chkData', 'xmlns:contact' => 'urn:ietf:params:xml:ns:contact-1.0') do - #xml.tag!('contact:id', @contact.code) - @contacts.each do |contact| + @results.each do |result| xml.tag!('contact:cd') do - xml.tag! "contact:id", contact[:code], avail: contact[:avail] - xml.tag!('contact:reason', contact[:reason]) unless contact[:avail] == 1 + xml.tag! "contact:id", result[:code], avail: result[:avail] + xml.tag!('contact:reason', result[:reason]) unless result[:avail] == 1 end end end diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 0f4560041..24480062d 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) @@ -18,18 +18,17 @@ 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:trDate', '123') if false - if @owner + 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 - 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/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/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..9134a9137 100755 --- a/bin/robot +++ b/bin/robot @@ -16,14 +16,14 @@ export RAILS_ENV=test cd "$(dirname "$0")"; cd .. cp config/application-example.yml config/application.yml -# create manually config/database.yml +cp config/secrets-example.yml config/secrets.yml +cp config/database-robot.yml config/database.yml +# under jenkins use rbenv-plugin wrapper 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,6 +36,10 @@ RCODE=$? echo "END_OF_RUBOCOP_RESULTS" echo "TEST_RESULTS" +# basic test +# ROBOT=true bundle exec rake + +# all tests with EPP ROBOT=true bundle exec rake test TCODE=$? echo "END_OF_TEST_RESULTS" @@ -45,7 +49,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 diff --git a/config/application-example.yml b/config/application-example.yml index 51a4661e5..31e5771fc 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -1,27 +1,29 @@ -defaults: &defaults - app_name: .EE Registry - zonefile_export_dir: 'export/zonefiles' +# Be sure to restart your server when you modify settings. - # 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 - ca_cert_path: ca-cert-path-here - ca_key_path: ca-key-path-here - ca_key_password: ca-key-pass-phrase-here +app_name: .EE Registry +zonefile_export_dir: 'export/zonefiles' -development: - <<: *defaults +# 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 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' +ca_key_password: 'your-root-key-password' + +# Used only by EPP server, you can leave it empty when running admin server: +webclient_ip: '127.0.0.1' + +# autotest config overwrites test: - <<: *defaults + 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' -alpha: - <<: *defaults - -staging: - <<: *defaults - -production: - <<: *defaults diff --git a/config/database-robot.yml b/config/database-robot.yml new file mode 100644 index 000000000..b38a048a2 --- /dev/null +++ b/config/database-robot.yml @@ -0,0 +1,19 @@ +default: &default + host: localhost + adapter: postgresql + encoding: unicode + pool: 5 + username: test + password: test + +test: + <<: *default + database: registry_test + +whois_test: + <<: *default + database: registry_whois_test + +api_log_test: + <<: *default + database: registry_api_log_test 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 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/initial_settings.rb b/config/initializers/initial_settings.rb index 97743775f..5458b6d2e 100644 --- a/config/initializers/initial_settings.rb +++ b/config/initializers/initial_settings.rb @@ -1,4 +1,5 @@ -if 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? 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'] diff --git a/config/locales/en.yml b/config/locales/en.yml index fc4ffbf3b..b2104e32c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,24 +1,3 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. - en: views: pagination: @@ -52,7 +31,6 @@ en: activerecord: errors: models: - contact: attributes: code: @@ -67,6 +45,8 @@ en: invalid: "Email is invalid" ident: blank: "Required parameter missing - ident" + invalid_EE_identity_format: "Ident not in valid Estonian identity format." + invalid_birthday_format: "Ident not in valid birthady format, should be YYYY-MM-DD" domains: exist: 'Object association prohibits operation' @@ -258,7 +238,7 @@ en: invalid_type: 'PostalInfo type is invalid' unimplemented_command: 'Unimplemented command' domain_exists_but_belongs_to_other_registrar: 'Domain exists but belongs to other registrar' - + required_attribute_missing: Required attributes missing code: 'Code' value: 'Value' @@ -321,7 +301,7 @@ en: domain_status_prohibits_deleting: 'Domain status prohibits deleting' domain_deleted: 'Domain deleted!' failed_to_delete_domain: 'Failed to delete domain!' - email: 'Email' + email: 'E-mail' fax: 'Fax' contact_details: 'Contact details' ident: 'Ident' @@ -330,8 +310,8 @@ en: country: 'Country' city: 'City' street: 'Street' - zip: 'Zip' - org_name: 'Organisation name' + zip: 'Postcode' + org_name: 'Org name' failed_to_add_domain: 'Failed to add domain!' domain_added: 'Domain added!' new_contact: 'New contact' @@ -502,3 +482,11 @@ en: download: 'Download' failed_to_create_certificate: 'Failed to create certificate!' registrant_not_found: 'Registrant not found' + 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 cd9f85b81..225ecaca2 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) @@ -47,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/20150212125339_add_state_to_address.rb b/db/migrate/20150212125339_add_state_to_address.rb new file mode 100644 index 000000000..a57412ce6 --- /dev/null +++ b/db/migrate/20150212125339_add_state_to_address.rb @@ -0,0 +1,5 @@ +class AddStateToAddress < ActiveRecord::Migration + def change + add_column :addresses, :state, :string + end +end diff --git a/db/migrate/20150217133755_add_country_code_ident.rb b/db/migrate/20150217133755_add_country_code_ident.rb new file mode 100644 index 000000000..a45abd2aa --- /dev/null +++ b/db/migrate/20150217133755_add_country_code_ident.rb @@ -0,0 +1,5 @@ +class AddCountryCodeIdent < ActiveRecord::Migration + def change + add_column :contacts, :ident_country_code, :string + end +end diff --git a/db/migrate/20150217133937_add_index_for_contact_code.rb b/db/migrate/20150217133937_add_index_for_contact_code.rb new file mode 100644 index 000000000..ff5a4a94b --- /dev/null +++ b/db/migrate/20150217133937_add_index_for_contact_code.rb @@ -0,0 +1,5 @@ +class AddIndexForContactCode < ActiveRecord::Migration + def change + add_index :contacts, :code + end +end diff --git a/db/migrate/20150223104842_create_certificates.rb b/db/migrate/20150223104842_create_certificates.rb new file mode 100644 index 000000000..b84e9b5ce --- /dev/null +++ b/db/migrate/20150223104842_create_certificates.rb @@ -0,0 +1,29 @@ +class CreateCertificates < ActiveRecord::Migration + def change + create_table :certificates do |t| + 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 + end +end 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/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/migrate/20150303151224_data_update_regisntrar_codes.rb b/db/migrate/20150303151224_data_update_regisntrar_codes.rb new file mode 100644 index 000000000..e49af65cb --- /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(validate: false) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 614fe9ae6..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: 20150213104014) do +ActiveRecord::Schema.define(version: 20150303151224) 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| @@ -52,6 +53,16 @@ ActiveRecord::Schema.define(version: 20150213104014) 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.string "creator_str" + t.string "updator_str" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "contact_disclosures", force: :cascade do |t| t.integer "contact_id" t.boolean "phone" @@ -95,8 +106,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" @@ -271,6 +285,18 @@ ActiveRecord::Schema.define(version: 20150213104014) 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 @@ -585,8 +611,11 @@ ActiveRecord::Schema.define(version: 20150213104014) 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" @@ -614,19 +643,19 @@ ActiveRecord::Schema.define(version: 20150213104014) 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/db/seeds.rb b/db/seeds.rb index ec49d6dbd..975dfa9b3 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! @@ -55,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']) 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/certificates.md b/doc/certificates.md new file mode 100644 index 000000000..c8a304f9f --- /dev/null +++ b/doc/certificates.md @@ -0,0 +1,196 @@ +Certificates setup +------------------ + +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 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 + 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 + +Generate the root key and remember your password, you need it later in application.yml: + + openssl genrsa -aes256 -out private/ca.key.pem 4096 + +Create root registry certificate (prompts for additional data and review days flag): + + 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 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 -days 3653 -key private/webclient.key.pem -out csrs/webclient.csr.pem + +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 -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 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' + ca_key_password: 'your-root-key-password' + + +### 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: + + 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" + +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 vi /etc/apache2/sites-enabled/webclient.conf + +Add these lines: + + SSLVerifyClient none + SSLVerifyDepth 1 + 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 + + 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 + + +### 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 +--------------- + +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 diff --git a/doc/epp/contact.md b/doc/epp/contact.md index 1117d0c86..db136c0db 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 @@ -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 @@ -30,12 +31,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 @@ -44,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 @@ -65,9 +64,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 +81,10 @@ Contact Mapping protocol short version: 0-1 Required if registrar is not the owner of the contact. 1 Contact password. Attribute: roid="String" 1 - 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" - 1 Base64 encoded document. + 1 Attribute: xmlns:eis="urn:ee:eis:xml:epp:eis-1.0" + 1 Base64 encoded document. Attribute: type="pdf/bdoc/ddoc/zip/rar/gz/tar/7z" - 0-1 Client transaction id + 0-1 Client transaction id [EXAMPLE REQUEST AND RESPONSE](/doc/epp-examples.md#epp-contact-with-valid-user-delete-command-deletes-contact) diff --git a/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 + diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 3108ad02f..fd35f1f23 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 @@ -19,16 +17,51 @@ namespace :db do task setup: [:environment] do Rake::Task['db:all:create'].invoke Rake::Task['db:all:schema:load'].invoke - Rake::Task['db:seed'].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' 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? + + Rake::Task['db:drop'].invoke + conf = ActiveRecord::Base.configurations + puts "#{conf[Rails.env]['database']} dropped" + + other_databases.each do |name| + begin + 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,10 +71,14 @@ 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" - ActiveRecord::Base.establish_connection(name) + 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!' else @@ -55,7 +92,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)}" 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') diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 62de51c5a..4690acfed 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -1,451 +1,441 @@ -# 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 = Fabricate(:contact, registrar: @registrar1) + + @legal_document = { + legalDocument: { + value: 'JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0Zp==', + attrs: { type: 'pdf' } + } + } + end + + context 'with valid user' do + context 'create command' do + def create_request(overwrites = {}) + defaults = { + postalInfo: { + name: { value: 'John Doe' }, + addr: { + street: { value: '123 Example' }, + city: { value: 'Tallinn' }, + cc: { value: 'EE' } + } + }, + voice: { value: '+372.1234567' }, + email: { value: 'test@example.example' }, + ident: { value: '37605030299', attrs: { type: 'priv', cc: 'EE' } } + } + create_xml = @epp_xml.create(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(create_xml, :xml) + end + + it 'fails if request xml is missing' do + response = epp_plain_request(@epp_xml.create, :xml) + response[:results][0][:msg].should == + 'Required parameter missing: create > create > postalInfo > name [name]' + response[:results][1][:msg].should == + 'Required parameter missing: create > create > postalInfo > addr > city [city]' + response[:results][2][:msg].should == + 'Required parameter missing: create > create > postalInfo > addr > cc [cc]' + response[:results][3][:msg].should == + 'Required parameter missing: create > create > ident [ident]' + response[:results][4][:msg].should == + 'Required parameter missing: create > create > voice [voice]' + response[:results][5][:msg].should == + 'Required parameter missing: create > create > email [email]' + response[:results][6][:msg].should == + 'Required parameter missing: extension > extdata > legalDocument [legal_document]' + + response[:results][0][:result_code].should == '2003' + response[:results][1][:result_code].should == '2003' + response[:results][2][:result_code].should == '2003' + response[:results][3][:result_code].should == '2003' + response[:results][4][:result_code].should == '2003' + response[:results][5][:result_code].should == '2003' + response[:results][6][:result_code].should == '2003' + + response[:results].count.should == 7 + end + + it 'successfully creates a contact' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + @contact = Contact.last + + @contact.registrar.should == @registrar1 + @registrar1.api_users.should include(@contact.creator) + @contact.ident.should == '37605030299' + @contact.address.street.should == '123 Example' + @contact.legal_documents.count.should == 1 + + log = ApiLog::EppLog.last + log.request_command.should == 'create' + log.request_object.should == 'contact' + log.request_successful.should == true + log.api_user_name.should == '1-api-registrar1' + log.api_user_registrar.should == 'registrar1' + end + + it 'successfully saves ident type' do + response = create_request( + { ident: { value: '1990-22-12', attrs: { type: 'birthday' } } } + ) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + Contact.last.ident_type.should == 'birthday' + end + + it 'successfully adds registrar' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + Contact.last.registrar.should == @registrar1 + end + + it 'returns result data upon success' do + response = create_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + id = response[:parsed].css('resData creData id').first + cr_date = response[:parsed].css('resData creData crDate').first + + id.text.length.should == 8 + # 5 seconds for what-ever weird lag reasons might happen + cr_date.text.to_time.should be_within(5).of(Time.now) + end + + 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 + before :all do + @contact = + Fabricate( + :contact, + registrar: @registrar1, + email: 'not_updated@test.test', + code: 'sh8013' + ) + end + + def update_request(overwrites = {}) + defaults = { + id: { value: 'asd123123er' }, + authInfo: { pw: { value: 'password' } }, + chg: { + postalInfo: { + name: { value: 'John Doe Edited' } + }, + voice: { value: '+372.7654321' }, + email: { value: 'edited@example.example' }, + disclose: { + value: { + voice: { value: '' }, + email: { value: '' } + }, attrs: { flag: '0' } + } + } + } + update_xml = @epp_xml.update(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(update_xml, :xml) + end + + it 'fails if request is invalid' do + response = epp_plain_request(@epp_xml.update, :xml) + + response[:results][0][:msg].should == + 'Required parameter missing: add, rem or chg' + response[:results][0][:result_code].should == '2003' + response[:results][1][:msg].should == + 'Required parameter missing: update > update > id [id]' + response[:results][1][:result_code].should == '2003' + response[:results][2][:msg].should == + '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 [legal_document]' + response[:results][3][:result_code].should == '2003' + response[:results].count.should == 4 + end + + it 'returns error if obj doesnt exist' do + response = update_request({ id: { value: 'not-exists' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results].count.should == 1 + end + + it 'is succesful' do + response = update_request({ id: { value: 'sh8013' } }) + + response[:msg].should == 'Command completed successfully' + @contact.reload + @contact.name.should == 'John Doe Edited' + @contact.email.should == 'edited@example.example' + end + + it 'fails with wrong authentication info' do + login_as :registrar2 do + response = update_request({ id: { value: 'sh8013' } }) + response[:msg].should == 'Authorization error' + response[:result_code].should == '2201' + end + end + + it 'returns phone and email error' do + response = update_request({ + id: { value: 'sh8013' }, + chg: { + voice: { value: '123213' }, + email: { value: 'wrong' } + } + }) + + response[:results][0][:msg].should == 'Phone nr is invalid [phone]' + response[:results][0][:result_code].should == '2005' + response[:results][1][:msg].should == 'Email is invalid [email]' + 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 + before do + @contact = Fabricate(:contact, registrar: @registrar1) + end + + def delete_request(overwrites = {}) + defaults = { + id: { value: @contact.code }, + authInfo: { pw: { value: @contact.auth_info } } + } + delete_xml = @epp_xml.delete(defaults.deep_merge(overwrites), @legal_document) + epp_plain_request(delete_xml, :xml) + end + + it 'fails if request is invalid' do + response = epp_plain_request(@epp_xml.delete, :xml) + + response[:results][0][:msg].should == + 'Required parameter missing: delete > delete > id [id]' + response[:results][0][:result_code].should == '2003' + response[:results][1][:msg].should == + '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 [legal_document]' + response[:results][2][:result_code].should == '2003' + response[:results].count.should == 3 + end + + it 'returns error if obj doesnt exist' do + response = delete_request({ id: { value: 'not-exists' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results].count.should == 1 + end + + it 'deletes contact' do + response = delete_request + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:clTRID].should == 'ABC-12345' + + Contact.find_by_id(@contact.id).should == nil + end + + it 'fails if contact has associated domain' do + @domain = Fabricate(:domain, registrar: @registrar1, owner_contact: @contact) + @domain.owner_contact.address.present?.should == true + + response = delete_request + response[:msg].should == 'Object association prohibits operation [domains]' + 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 [id]' + response[:results][0][:result_code].should == '2003' + response[:results].count.should == 1 + end + + it 'returns info about contact availability' do + Fabricate(:contact, code: 'check-1234') + + response = epp_plain_request(check_multiple_contacts_xml, :xml) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + ids = response[:parsed].css('resData chkData id') + + ids[0].attributes['avail'].text.should == '0' + ids[1].attributes['avail'].text.should == '1' + + ids[0].text.should == 'check-1234' + ids[1].text.should == 'check-4321' + end + end + + context 'info command' do + def info_request(overwrites = {}) + defaults = { + id: { value: @contact.code }, + authInfo: { pw: { value: @contact.auth_info } } + } + xml = @epp_xml.info(defaults.deep_merge(overwrites)) + epp_plain_request(xml, :xml) + end + + it 'fails if request invalid' do + response = epp_plain_request(@epp_xml.info, :xml) + response[:results][0][:msg].should == + 'Required parameter missing: info > info > id [id]' + response[:results][0][:result_code].should == '2003' + response[:results].count.should == 1 + end + + it 'returns error when object does not exist' do + response = info_request({ id: { value: 'no-contact' } }) + response[:msg].should == 'Object does not exist' + response[:result_code].should == '2303' + response[:results][0][:value].should == 'no-contact' + response[:results].count.should == 1 + end + + it 'return info about contact' do + @registrar1_contact = Fabricate( + :contact, code: 'info-4444', registrar: @registrar1, + name: 'Johnny Awesome', address: Fabricate(:address)) + + response = info_request({ id: { value: @registrar1_contact.code } }) + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + + contact = response[:parsed].css('resData chkData') + contact.css('name').first.text.should == 'Johnny Awesome' + end + + it 'returns no authorization error for wrong password when owner' do + response = info_request({ authInfo: { pw: { value: 'wrong-pw' } } }) + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:results].count.should == 1 + end + + it 'returns no authorization error for wrong user but correct pw' do + login_as :registrar2 do + response = info_request + + response[:msg].should == 'Command completed successfully' + response[:result_code].should == '1000' + response[:results].count.should == 1 + end + end + + it 'returns authorization error for wrong user and wrong pw' do + login_as :registrar2 do + response = info_request({ authInfo: { pw: { value: 'wrong-pw' } } }) + response[:msg].should == 'Authorization error' + response[:result_code].should == '2201' + response[:results].count.should == 1 + end + end + end + + context 'renew command' do + it 'returns 2101-unimplemented command' do + response = epp_plain_request('contacts/renew.xml') + + response[:msg].should == 'Unimplemented command' + response[:result_code].should == '2101' + end + end + end + + def check_multiple_contacts_xml + ' + + + + + check-1234 + check-4321 + + + ABC-12345 + + ' + end +end diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 5ba4c4159..1b03e2487 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -11,12 +11,10 @@ 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') - 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 } @@ -61,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 @@ -169,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 @@ -191,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 @@ -200,14 +202,23 @@ 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 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 [ns]' + response[:results][0][:result_code].should == '2003' + + response[:results][1][:msg].should == + 'Required parameter missing: create > create > ns > hostAttr [host_attr]' + response[:results][1][:result_code].should == '2003' + + response[:results].count.should == 2 end it 'does not create domain with too many nameservers' do @@ -226,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 @@ -246,8 +257,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) + response[:msg].should == 'Hostname is invalid [hostname]' response[:result_code].should == '2005' - response[:msg].should == 'Hostname is invalid' end it 'checks hostAttr presence' do @@ -263,8 +274,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) + response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr [host_attr]' response[:result_code].should == '2003' - response[:msg].should == 'Required parameter missing: create > create > ns > hostAttr' end it 'creates domain with nameservers with ips' do @@ -280,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 @@ -294,8 +305,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 @@ -306,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 @@ -391,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 @@ -435,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 @@ -464,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 @@ -661,8 +673,8 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml, :xml) + response[:msg].should == 'Admin contacts count must be between 1-10 [admin_contacts]' response[:result_code].should == '2004' - response[:msg].should == 'Admin contacts count must be between 1-10' response[:clTRID].should == 'ABC-12345' Domain.count.should == domain_count @@ -678,8 +690,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 @@ -853,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 @@ -970,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 @@ -1017,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 @@ -1120,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 @@ -1292,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 @@ -1309,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 @@ -1366,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 @@ -1518,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 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 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/fabricators/contact_fabricator.rb b/spec/fabricators/contact_fabricator.rb index c483ad554..45c4db75e 100644 --- a/spec/fabricators/contact_fabricator.rb +++ b/spec/fabricators/contact_fabricator.rb @@ -1,12 +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' - code { "sh#{Faker::Number.number(8)}" } - ident_type 'op' - auth_info 'ccds4324pok' + ident_type 'priv' + ident_country_code 'EE' 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/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/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 diff --git a/spec/models/certificate_spec.rb b/spec/models/certificate_spec.rb new file mode 100644 index 000000000..26656e51b --- /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', epp: true do + @certificate.status.should == 'unsigned' + @certificate.sign! + @certificate.status.should == 'signed' + @certificate.crt.should_not be_blank + end + + it 'should revoke crt', epp: true 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 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..dbf7bc3dc 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -39,11 +39,11 @@ describe Contact do end it 'should not have creator' do - @contact.cr_id.should == nil + @contact.creator.should == nil end it 'should not have updater' do - @contact.up_id.should == nil + @contact.updator.should == nil end it 'phone should return false' do @@ -52,9 +52,51 @@ 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 + + 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 @@ -87,18 +129,23 @@ 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 'bic should be valid' do + @contact.ident_type = 'bic' + @contact.ident = '1234' + @contact.valid? + @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 '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 @@ -119,7 +166,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 @@ -151,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 @@ -174,62 +258,13 @@ 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 - - context 'with creator' do - before :all do - # @contact.created_by = @api_user - end - - # TODO: change cr_id to something else - it 'should return username of creator' do - # @contact.cr_id.should == 'gitlab' - end - end - - context 'with updater' do - before :all do - # @contact.updated_by = @api_user - end - - # TODO: change up_id to something else - it 'should return username of updater' do - # @contact.up_id.should == 'gitlab' - end - - end end end end -# TODO: investigate it a bit more -# describe Contact, '#relations_with_domain?' do -# context 'with relation' do -# before :all do -# create_settings -# Fabricate(:domain) -# @contact = Fabricate(:contact) -# end - -# it 'should have relation with domain' do -# @contact.relations_with_domain?.should == true -# end -# end -# end - -describe Contact, '.extract_params' do - it 'returns params hash'do - ph = { id: '123123', email: 'jdoe@example.com', authInfo: { pw: 'asde' }, - postalInfo: { name: 'fred', addr: { cc: 'EE' } } } - Contact.extract_attributes(ph).should == { - name: 'fred', - email: 'jdoe@example.com' - } - end -end - describe Contact, '.check_availability' do before do Fabricate(:contact, code: 'asd12') diff --git a/spec/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 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 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