diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ccc6a77..9be8824da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +10.05.2021 +* Domain update confirmation fix [#1975](https://github.com/internetee/registry/pull/1975) +* Bump bootsnap to 1.7.5 [#1970](https://github.com/internetee/registry/pull/1970) +* Bump truemail to 2.4.1 [#1971](https://github.com/internetee/registry/pull/1971) +* Bump devise to 4.8.0 [#1972](https://github.com/internetee/registry/pull/1972) +* Bump iso8601 to 0.13.0 [#1973](https://github.com/internetee/registry/pull/1973) +* Bump puma to 5.3.0 [#1974](https://github.com/internetee/registry/pull/1974) + +06.05.2021 +* List all unread polls option for REPP [#1936](https://github.com/internetee/registry/pull/1936) +* Bump Rails to 6.1.3.1 [#1962](https://github.com/internetee/registry/pull/1962) +* Bump mimemagic to 0.4.3 [](https://github.com/internetee/registry/pull/1960) + +03.05.2021 +* Imporved error handling on invalid XML over EPP [#1952](https://github.com/internetee/registry/pull/1952) +* Bump bootsnap to 1.7.4 [#1963](https://github.com/internetee/registry/pull/1963) +* Bump truemail to 2.4.0 [#1964](https://github.com/internetee/registry/pull/1964) + +30.04.2021 +* Fixed error message on oversized legaldocs [#1880](https://github.com/internetee/registry/issues/1880) + +29.04.2021 +* Admin is able to cancel invoice payments [#1937](https://github.com/internetee/registry/issues/1937) +* Bump nokogiri to 1.11.3 [#1920](https://github.com/internetee/registry/pull/1920) + 26.04.2021 * Disputed status is removed on registrant change and status added to schema [#1927](https://github.com/internetee/registry/issues/1927) * Bounce list record is removed one there are no active contacts with the address [#1912](https://github.com/internetee/registry/issues/1912) diff --git a/Gemfile b/Gemfile index 85d62654c..9b23d89b3 100644 --- a/Gemfile +++ b/Gemfile @@ -4,10 +4,10 @@ source 'https://rubygems.org' gem 'active_interaction', '~> 4.0' gem 'apipie-rails', '~> 0.5.18' gem 'bootsnap', '>= 1.1.0', require: false -gem 'iso8601', '0.12.1' # for dates and times +gem 'iso8601', '0.13.0' # for dates and times gem 'mime-types-data' -gem 'mimemagic', '0.3.10' -gem 'rails', '~> 6.0' +gem 'mimemagic', '0.4.3' +gem 'rails', '~> 6.1.3.2' gem 'rest-client' gem 'uglifier' @@ -20,12 +20,12 @@ gem 'paper_trail', '~> 12.0' gem 'pg', '1.2.3' # 1.8 is for Rails < 5.0 gem 'ransack', '~> 2.3' -gem 'truemail', '~> 2.3' # validates email by regexp, mail server existence and address existence +gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence gem 'validates_email_format_of', '1.6.3' # validates email against RFC 2822 and RFC 3696 # 0.7.3 is the latest for Rails 4.2, however, it is absent on Rubygems server # https://github.com/huacnlee/rails-settings-cached/issues/165 -gem 'nokogiri', '~> 1.11.0' +gem 'nokogiri', '~> 1.11.3' # style gem 'bootstrap-sass', '~> 3.4' @@ -37,7 +37,7 @@ gem 'kaminari' gem 'sass-rails' gem 'select2-rails', '4.0.13' # for autocomplete gem 'cancancan' -gem 'devise', '~> 4.7' +gem 'devise', '~> 4.8' # registry specfic gem 'data_migrate', '~> 7.0' diff --git a/Gemfile.lock b/Gemfile.lock index dd41719ad..2c856ddb5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,65 +76,69 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (6.0.3.6) - actionpack (= 6.0.3.6) + actioncable (6.1.3.2) + actionpack (= 6.1.3.2) + activesupport (= 6.1.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.3.6) - actionpack (= 6.0.3.6) - activejob (= 6.0.3.6) - activerecord (= 6.0.3.6) - activestorage (= 6.0.3.6) - activesupport (= 6.0.3.6) + actionmailbox (6.1.3.2) + actionpack (= 6.1.3.2) + activejob (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) mail (>= 2.7.1) - actionmailer (6.0.3.6) - actionpack (= 6.0.3.6) - actionview (= 6.0.3.6) - activejob (= 6.0.3.6) + actionmailer (6.1.3.2) + actionpack (= 6.1.3.2) + actionview (= 6.1.3.2) + activejob (= 6.1.3.2) + activesupport (= 6.1.3.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.3.6) - actionview (= 6.0.3.6) - activesupport (= 6.0.3.6) - rack (~> 2.0, >= 2.0.8) + actionpack (6.1.3.2) + actionview (= 6.1.3.2) + activesupport (= 6.1.3.2) + rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.3.6) - actionpack (= 6.0.3.6) - activerecord (= 6.0.3.6) - activestorage (= 6.0.3.6) - activesupport (= 6.0.3.6) + actiontext (6.1.3.2) + actionpack (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) nokogiri (>= 1.8.5) - actionview (6.0.3.6) - activesupport (= 6.0.3.6) + actionview (6.1.3.2) + activesupport (= 6.1.3.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_interaction (4.0.0) activemodel (>= 5, < 7) - activejob (6.0.3.6) - activesupport (= 6.0.3.6) + activejob (6.1.3.2) + activesupport (= 6.1.3.2) globalid (>= 0.3.6) - activemodel (6.0.3.6) - activesupport (= 6.0.3.6) - activerecord (6.0.3.6) - activemodel (= 6.0.3.6) - activesupport (= 6.0.3.6) + activemodel (6.1.3.2) + activesupport (= 6.1.3.2) + activerecord (6.1.3.2) + activemodel (= 6.1.3.2) + activesupport (= 6.1.3.2) activerecord-import (1.0.8) activerecord (>= 3.2) - activestorage (6.0.3.6) - actionpack (= 6.0.3.6) - activejob (= 6.0.3.6) - activerecord (= 6.0.3.6) + activestorage (6.1.3.2) + actionpack (= 6.1.3.2) + activejob (= 6.1.3.2) + activerecord (= 6.1.3.2) + activesupport (= 6.1.3.2) marcel (~> 1.0.0) - activesupport (6.0.3.6) + mini_mime (~> 1.0.2) + activesupport (6.1.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) aes_key_wrap (1.1.0) @@ -164,7 +168,7 @@ GEM aws-eventstream (~> 1, >= 1.0.2) bcrypt (3.1.16) bindata (2.4.8) - bootsnap (1.7.3) + bootsnap (1.7.5) msgpack (~> 1.0) bootstrap-sass (3.4.1) autoprefixer-rails (>= 5.2.1) @@ -211,7 +215,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - devise (4.7.3) + devise (4.8.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -249,7 +253,7 @@ GEM concurrent-ruby (~> 1.0) i18n_data (0.11.0) isikukood (0.1.2) - iso8601 (0.12.1) + iso8601 (0.13.0) jmespath (1.4.0) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) @@ -286,10 +290,10 @@ GEM mime-types (3.3.1) mime-types-data (~> 3.2015) mime-types-data (3.2021.0225) - mimemagic (0.3.10) + mimemagic (0.4.3) nokogiri (~> 1) rake - mini_mime (1.1.0) + mini_mime (1.0.3) mini_portile2 (2.5.1) minitest (5.14.4) monetize (1.9.4) @@ -307,10 +311,10 @@ GEM ruby2_keywords (~> 0.0.1) netrc (0.11.0) nio4r (2.5.7) - nokogiri (1.11.0) + nokogiri (1.11.3) mini_portile2 (~> 2.5.0) racc (~> 1.4) - nokogiri (1.11.0-x86_64-linux) + nokogiri (1.11.3-x86_64-linux) racc (~> 1.4) nori (2.6.0) omniauth (1.9.1) @@ -339,7 +343,7 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (4.0.6) - puma (5.2.2) + puma (5.3.0) nio4r (~> 2.0) que (0.14.3) que-web (0.7.2) @@ -358,32 +362,32 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.0.3.6) - actioncable (= 6.0.3.6) - actionmailbox (= 6.0.3.6) - actionmailer (= 6.0.3.6) - actionpack (= 6.0.3.6) - actiontext (= 6.0.3.6) - actionview (= 6.0.3.6) - activejob (= 6.0.3.6) - activemodel (= 6.0.3.6) - activerecord (= 6.0.3.6) - activestorage (= 6.0.3.6) - activesupport (= 6.0.3.6) - bundler (>= 1.3.0) - railties (= 6.0.3.6) + rails (6.1.3.2) + actioncable (= 6.1.3.2) + actionmailbox (= 6.1.3.2) + actionmailer (= 6.1.3.2) + actionpack (= 6.1.3.2) + actiontext (= 6.1.3.2) + actionview (= 6.1.3.2) + activejob (= 6.1.3.2) + activemodel (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) + bundler (>= 1.15.0) + railties (= 6.1.3.2) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (6.0.3.6) - actionpack (= 6.0.3.6) - activesupport (= 6.0.3.6) + railties (6.1.3.2) + actionpack (= 6.1.3.2) + activesupport (= 6.1.3.2) method_source rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) + thor (~> 1.0) rake (13.0.3) ransack (2.4.2) activerecord (>= 5.2.4) @@ -459,12 +463,11 @@ GEM httpclient (>= 2.4) temple (0.8.2) thor (1.1.0) - thread_safe (0.3.6) tilt (2.0.10) - truemail (2.3.4) + truemail (2.4.1) simpleidn (~> 0.2.1) - tzinfo (1.2.9) - thread_safe (~> 0.1) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) unf (0.1.4) @@ -527,7 +530,7 @@ DEPENDENCIES daemons-rails (= 1.2.1) data_migrate (~> 7.0) database_cleaner - devise (~> 4.7) + devise (~> 4.8) digidoc_client! directo! dnsruby (~> 1.61) @@ -538,16 +541,16 @@ DEPENDENCIES figaro (~> 1.2) haml (~> 5.0) isikukood - iso8601 (= 0.12.1) + iso8601 (= 0.13.0) jquery-rails jquery-ui-rails (= 6.0.1) kaminari lhv! mime-types-data - mimemagic (= 0.3.10) + mimemagic (= 0.4.3) minitest (~> 5.14) money-rails - nokogiri (~> 1.11.0) + nokogiri (~> 1.11.3) omniauth omniauth-rails_csrf_protection omniauth-tara! @@ -558,7 +561,7 @@ DEPENDENCIES puma que que-web - rails (~> 6.0) + rails (~> 6.1.3.2) ransack (~> 2.3) rest-client rexml @@ -568,7 +571,7 @@ DEPENDENCIES sidekiq simplecov (= 0.17.1) simpleidn (= 0.2.1) - truemail (~> 2.3) + truemail (~> 2.4) uglifier validates_email_format_of (= 1.6.3) webdrivers @@ -577,4 +580,4 @@ DEPENDENCIES wkhtmltopdf-binary (~> 0.12.5.1) BUNDLED WITH - 2.2.15 + 2.2.17 diff --git a/README.md b/README.md index ed68e62af..d0766bd15 100644 --- a/README.md +++ b/README.md @@ -396,10 +396,9 @@ sudo apt-get install libxext-dev libxrender1 fontconfig * [Testing](/doc/testing.md) -### Travis CI +### Github Actions CI -* Travis is configured to build against master and staging branches by default. -* Notification emails are sent to committer by default. +* Github Actions CI is configured to build all the PRs. ### EPP web client diff --git a/app/controllers/admin/admin_users_controller.rb b/app/controllers/admin/admin_users_controller.rb index 8e72fd274..3d0c4280b 100644 --- a/app/controllers/admin/admin_users_controller.rb +++ b/app/controllers/admin/admin_users_controller.rb @@ -34,7 +34,7 @@ module Admin params[:admin_user].delete(:password) if params[:admin_user][:password].blank? params[:admin_user].delete(:password_confirmation) if params[:admin_user][:password_confirmation].blank? - if @admin_user.update_attributes(admin_user_params) + if @admin_user.update(admin_user_params) flash[:notice] = I18n.t('record_updated') redirect_to [:admin, @admin_user] else diff --git a/app/controllers/admin/billing/prices_controller.rb b/app/controllers/admin/billing/prices_controller.rb index 609ebd21e..4c1e2e30a 100644 --- a/app/controllers/admin/billing/prices_controller.rb +++ b/app/controllers/admin/billing/prices_controller.rb @@ -40,7 +40,6 @@ module Admin def create @price = ::Billing::Price.new(price_params) - if @price.save flash[:notice] = t('.created') redirect_to_index @@ -50,7 +49,7 @@ module Admin end def update - if @price.update_attributes(price_params) + if @price.update(price_params.compact_blank) flash[:notice] = t('.updated') redirect_to_index else @@ -81,7 +80,11 @@ module Admin valid_to ] - params.require(:price).permit(*allowed_params) + allowed = params.require(:price).permit(*allowed_params) + if allowed[:duration] + allowed[:duration] = ActiveSupport::Duration.build(allowed[:duration].to_i) + end + allowed end def search_params @@ -104,8 +107,7 @@ module Admin end def durations - durations = ::Billing::Price::durations - durations.collect { |duration| [duration.sub('mon', 'month'), duration] } + ::Billing::Price::durations end def statuses diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index c9f1d0c46..bd54ffd0b 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -20,6 +20,18 @@ module Admin end end + def cancel_paid + invoice_id = params[:invoice_id] + invoice = Invoice.find(invoice_id) + + if account_activity_with_negative_sum(invoice) + flash[:notice] = t(:payment_was_cancelled) + else + flash[:alert] = t(:failed_to_payment_cancel) + end + redirect_to admin_invoices_path + end + def index @q = Invoice.includes(:account_activity).search(params[:q]) @q.sorts = 'number desc' if @q.sorts.empty? @@ -43,5 +55,21 @@ module Admin def deposit_params params.require(:deposit).permit(:amount, :description, :registrar_id) end + + def account_activity_with_negative_sum(invoice) + account_activity = AccountActivity.find_by(invoice_id: invoice.id) + account_activity_dup = account_activity.dup + account_activity_dup.sum = -account_activity.sum.to_i + account_activity_dup.save + account_activity.update(invoice_id: nil) + account_activity_dup.update(invoice_id: nil) + mark_cancelled_payment_order(invoice) + account_activity.save && account_activity_dup.save + end + + def mark_cancelled_payment_order(invoice) + payment_order = invoice.payment_orders.last + payment_order.update(notes: 'Cancelled') + end end end diff --git a/app/controllers/epp/base_controller.rb b/app/controllers/epp/base_controller.rb index 99c0ead35..3230d3e70 100644 --- a/app/controllers/epp/base_controller.rb +++ b/app/controllers/epp/base_controller.rb @@ -23,30 +23,29 @@ module Epp rescue_from ActiveRecord::RecordNotFound, with: :respond_with_object_does_not_exist_error before_action :set_paper_trail_whodunnit + skip_before_action :validate_against_schema + protected def respond_with_command_failed_error(exception) - epp_errors << { - code: '2400', - msg: 'Command failed', - } + epp_errors.add(:epp_errors, + code: '2400', + message: 'Command failed') handle_errors log_exception(exception) end def respond_with_object_does_not_exist_error - epp_errors << { - code: '2303', - msg: 'Object does not exist', - } + epp_errors.add(:epp_errors, + code: '2303', + msg: 'Object does not exist') handle_errors end def respond_with_authorization_error - epp_errors << { - code: '2201', - msg: 'Authorization error', - } + epp_errors.add(:epp_errors, + code: '2201', + msg: 'Authorization error') handle_errors end @@ -61,10 +60,9 @@ module Epp def validate_against_schema return if %w[hello error].include?(params[:action]) schema.validate(params[:nokogiri_frame]).each do |error| - epp_errors << { - code: 2001, - msg: error - } + epp_errors.add(:epp_errors, + code: 2001, + msg: error) end handle_errors and return if epp_errors.any? end @@ -92,30 +90,17 @@ module Epp # ERROR + RESPONSE HANDLING def epp_errors - @errors ||= [] + @errors ||= ActiveModel::Errors.new(self) end def handle_errors(obj = nil) - @errors ||= [] + @errors ||= ActiveModel::Errors.new(self) if obj obj.construct_epp_errors - @errors += obj.errors[:epp_errors] + obj.errors.each { |error| @errors.import error } end - if params[:parsed_frame].at_css('update') - @errors.each_with_index do |errors, index| - if errors[:code] == '2304' && - errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' - @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE - end - end - end - - @errors.uniq! - render_epp_response '/epp/error' end @@ -132,10 +117,9 @@ module Epp return true end - epp_errors << { - msg: 'Parameter value policy error. Allowed only Latin characters.', - code: '2306' - } + epp_errors.add(:epp_errors, + msg: 'Parameter value policy error. Allowed only Latin characters.', + code: '2306') handle_errors and return false end @@ -179,10 +163,12 @@ module Epp else missing = el.present? ? el.text.blank? : true end - epp_errors << { - code: '2003', - msg: I18n.t('errors.messages.required_parameter_missing', key: "#{full_selector} [#{attr}]") - } if missing + next unless missing + + epp_errors.add(:epp_errors, + code: '2003', + message: I18n.t('errors.messages.required_parameter_missing', + key: "#{full_selector} [#{attr}]")) end missing ? false : el # return last selector if it was present @@ -200,25 +186,23 @@ module Epp attribute = element[attribute_selector] unless attribute - epp_errors << { - code: '2003', - msg: I18n.t('errors.messages.required_parameter_missing', key: attribute_selector) - } + epp_errors.add(:epp_errors, + code: '2003', + msg: I18n.t('errors.messages.required_parameter_missing', + key: attribute_selector)) return end return if options[:values].include?(attribute) if options[:policy] - epp_errors << { - code: '2306', - msg: I18n.t('attribute_is_invalid', attribute: attribute_selector) - } + epp_errors.add(:epp_errors, + code: '2306', + msg: I18n.t('attribute_is_invalid', attribute: attribute_selector)) else - epp_errors << { - code: '2004', - msg: I18n.t('parameter_value_range_error', key: attribute_selector) - } + epp_errors.add(:epp_errors, + code: '2004', + msg: I18n.t('parameter_value_range_error', key: attribute_selector)) end end @@ -230,30 +214,29 @@ module Epp attribute = element[attribute_selector] return if (attribute && options[:values].include?(attribute)) || !attribute - epp_errors << { - code: '2306', - msg: I18n.t('attribute_is_invalid', attribute: attribute_selector) - } + epp_errors.add(:epp_errors, + code: '2306', + msg: I18n.t('attribute_is_invalid', attribute: attribute_selector)) end def exactly_one_of(*selectors) full_selectors = create_full_selectors(*selectors) return if element_count(*full_selectors, use_prefix: false) == 1 - epp_errors << { - code: '2306', - msg: I18n.t(:exactly_one_parameter_required, params: full_selectors.join(' OR ')) - } + epp_errors.add(:epp_errors, + code: '2306', + msg: I18n.t(:exactly_one_parameter_required, + params: full_selectors.join(' OR '))) end def mutually_exclusive(*selectors) full_selectors = create_full_selectors(*selectors) return if element_count(*full_selectors, use_prefix: false) <= 1 - epp_errors << { - code: '2306', - msg: I18n.t(:mutally_exclusive_params, params: full_selectors.join(', ')) - } + epp_errors.add(:epp_errors, + code: '2306', + msg: I18n.t(:mutally_exclusive_params, + params: full_selectors.join(', '))) end def optional(selector, *validations) @@ -264,8 +247,8 @@ module Epp validations.each do |x| validator = "#{x.first[0]}_validator".camelize.constantize - err = validator.validate_epp(selector.split(' ').last, value) - epp_errors << err if err + result = validator.validate_epp(selector.split(' ').last, value) + epp_errors.add(:epp_errors, result) if result end end @@ -296,10 +279,11 @@ module Epp def xml_attrs_present?(ph, attributes) # TODO: THIS IS DEPRECATED AND WILL BE REMOVED IN FUTURE attributes.each do |x| - epp_errors << { - code: '2003', - msg: I18n.t('errors.messages.required_parameter_missing', key: x.last) - } unless has_attribute(ph, x) + next if has_attribute(ph, x) + + epp_errors.add(:epp_errors, + code: '2003', + msg: I18n.t('errors.messages.required_parameter_missing', key: x.last)) end epp_errors.empty? end @@ -354,10 +338,9 @@ module Epp def enforce_epp_session_timeout if epp_session.timed_out? - epp_errors << { - code: '2201', - msg: 'Authorization error: Session timeout', - } + epp_errors.add(:epp_errors, + code: '2201', + msg: 'Authorization error: Session timeout') handle_errors epp_session.destroy! else diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 65354ff48..10250563c 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -55,13 +55,13 @@ module Epp def renew authorize! :renew, Epp::Contact - epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') } + epp_errors.add(:epp_errors, code: '2101', msg: t(:'errors.messages.unimplemented_command')) handle_errors end def transfer authorize! :transfer, Epp::Contact - epp_errors << { code: '2101', msg: t(:'errors.messages.unimplemented_command') } + epp_errors.add(:epp_errors, code: '2101', msg: t(:'errors.messages.unimplemented_command')) handle_errors end @@ -72,9 +72,10 @@ module Epp end def action_call_response(action:) - # rubocop:disable Style/AndOr - (handle_errors(@contact) and return) unless action.call - # rubocop:enable Style/AndOr + unless action.call + handle_errors(@contact) + return + end if opt_addr? @response_code = 1100 @@ -134,24 +135,16 @@ module Epp ident = params[:parsed_frame].css('ident') if ident.present? && ident.attr('type').blank? - epp_errors << { - code: '2003', - msg: I18n.t('errors.messages.required_ident_attribute_missing', key: 'type') - } + epp_errors.add(:epp_errors, + code: '2003', + msg: I18n.t('errors.messages.required_ident_attribute_missing', key: 'type')) end if ident.present? && ident.text != 'birthday' && ident.attr('cc').blank? - epp_errors << { - code: '2003', - msg: I18n.t('errors.messages.required_ident_attribute_missing', key: 'cc') - } + epp_errors.add(:epp_errors, + code: '2003', + msg: I18n.t('errors.messages.required_ident_attribute_missing', key: 'cc')) end - # if ident.present? && ident.attr('cc').blank? - # epp_errors << { - # code: '2003', - # msg: I18n.t('errors.messages.required_ident_attribute_missing', key: 'cc') - # } - # end contact_org_disabled fax_disabled status_editing_disabled @@ -178,28 +171,25 @@ module Epp return true if ENV['contact_org_enabled'] == 'true' return true if params[:parsed_frame].css('postalInfo org').text.blank? - epp_errors << { - code: '2306', - msg: "#{I18n.t(:contact_org_error)}: postalInfo > org [org]" - } + epp_errors.add(:epp_errors, + code: '2306', + msg: "#{I18n.t(:contact_org_error)}: postalInfo > org [org]") end def fax_disabled return true if ENV['fax_enabled'] == 'true' return true if params[:parsed_frame].css('fax').text.blank? - epp_errors << { - code: '2306', - msg: "#{I18n.t(:contact_fax_error)}: fax [fax]" - } + epp_errors.add(:epp_errors, + code: '2306', + msg: "#{I18n.t(:contact_fax_error)}: fax [fax]") end def status_editing_disabled return true if Setting.client_status_editing_enabled return true if params[:parsed_frame].css('status').empty? - epp_errors << { - code: '2306', - msg: "#{I18n.t(:client_side_status_editing_error)}: status [status]" - } + epp_errors.add(:epp_errors, + code: '2306', + msg: "#{I18n.t(:client_side_status_editing_error)}: status [status]") end def address_given? diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index 47a40857a..6f8d10ced 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -44,7 +44,10 @@ module Epp update_params = ::Deserializers::Xml::DomainUpdate.new(params[:parsed_frame], registrar_id).call action = Actions::DomainUpdate.new(@domain, update_params, false) - (handle_errors(@domain) and return) unless action.call + unless action.call + handle_errors(@domain) + return + end pending = @domain.epp_pending_update.present? render_epp_response("/epp/domains/success#{'_pending' if pending}") @@ -90,10 +93,9 @@ module Epp action = params[:parsed_frame].css('transfer').first[:op] if @domain.non_transferable? - epp_errors << { - code: '2304', - msg: I18n.t(:object_status_prohibits_operation), - } + epp_errors.add(:epp_errors, + code: '2304', + msg: I18n.t(:object_status_prohibits_operation)) handle_errors return end @@ -102,10 +104,9 @@ module Epp wrong_transfer_code = provided_transfer_code != @domain.transfer_code if wrong_transfer_code - epp_errors << { - code: '2202', - msg: 'Invalid authorization information', - } + epp_errors.add(:epp_errors, + code: '2202', + msg: 'Invalid authorization information') handle_errors return end @@ -120,10 +121,9 @@ module Epp if @domain_transfer render_epp_response '/epp/domains/transfer' else - epp_errors << { - code: '2303', - msg: I18n.t('no_transfers_found') - } + epp_errors.add(:epp_errors, + code: '2303', + msg: I18n.t('no_transfers_found')) handle_errors end end @@ -184,11 +184,10 @@ module Epp def validate_transfer # period element is disabled for now if params[:parsed_frame].css('period').any? - epp_errors << { - code: '2307', - msg: I18n.t(:unimplemented_object_service), - value: { obj: 'period' } - } + epp_errors.add(:epp_errors, + code: '2307', + msg: I18n.t(:unimplemented_object_service), + value: { obj: 'period' }) end requires 'transfer > transfer' @@ -217,10 +216,9 @@ module Epp return true if Setting.client_status_editing_enabled return true if check_client_hold return true if params[:parsed_frame].css('status').empty? - epp_errors << { - code: '2306', - msg: "#{I18n.t(:client_side_status_editing_error)}: status [status]" - } + epp_errors.add(:epp_errors, + code: '2306', + msg: "#{I18n.t(:client_side_status_editing_error)}: status [status]") end def check_client_hold @@ -232,17 +230,15 @@ module Epp @domain_pricelist = @domain.pricelist(operation, period.try(:to_i), unit) if @domain_pricelist.try(:price) # checking if price list is not found if current_user.registrar.balance < @domain_pricelist.price.amount - epp_errors << { - code: '2104', - msg: I18n.t('billing_failure_credit_balance_low') - } + epp_errors.add(:epp_errors, + code: '2104', + msg: I18n.t('billing_failure_credit_balance_low')) return false end else - epp_errors << { - code: '2104', - msg: I18n.t(:active_price_missing_for_this_operation) - } + epp_errors.add(:epp_errors, + code: '2104', + msg: I18n.t(:active_price_missing_for_this_operation)) return false end true diff --git a/app/controllers/epp/errors_controller.rb b/app/controllers/epp/errors_controller.rb index e4b179464..ab2e00b75 100644 --- a/app/controllers/epp/errors_controller.rb +++ b/app/controllers/epp/errors_controller.rb @@ -3,7 +3,12 @@ module Epp skip_authorization_check def error - epp_errors << { code: params[:code], msg: params[:msg] } + epp_errors.add(:epp_errors, code: params[:code], msg: params[:msg]) + render_epp_response '/epp/error' + end + + def command_handler + epp_errors.add(:epp_errors, code: '2000', msg: 'Unknown command') render_epp_response '/epp/error' end end diff --git a/app/controllers/epp/polls_controller.rb b/app/controllers/epp/polls_controller.rb index a93fa300d..86218336b 100644 --- a/app/controllers/epp/polls_controller.rb +++ b/app/controllers/epp/polls_controller.rb @@ -43,11 +43,11 @@ module Epp @notification = current_user.unread_notifications.find_by(id: params[:parsed_frame].css('poll').first['msgID']) unless @notification - epp_errors << { - code: '2303', - msg: I18n.t('message_was_not_found'), - value: { obj: 'msgID', val: params[:parsed_frame].css('poll').first['msgID'] } - } + epp_errors.add(:epp_errors, + code: '2303', + msg: I18n.t('message_was_not_found'), + value: { obj: 'msgID', + val: params[:parsed_frame].css('poll').first['msgID'] }) handle_errors and return end diff --git a/app/controllers/epp/sessions_controller.rb b/app/controllers/epp/sessions_controller.rb index 04603dbe7..7e540bb89 100644 --- a/app/controllers/epp/sessions_controller.rb +++ b/app/controllers/epp/sessions_controller.rb @@ -20,10 +20,10 @@ module Epp server_md5 = Certificate.parse_md_from_string(File.read(ENV['cert_path'])) if client_md5 != server_md5 - epp_errors << { - msg: 'Authentication error; server closing connection (certificate is not valid)', - code: '2501' - } + msg = 'Authentication error; server closing connection (certificate is not valid)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end @@ -32,56 +32,56 @@ module Epp if !Rails.env.development? && (!webclient_request && @api_user) unless @api_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], request.env['HTTP_SSL_CLIENT_S_DN_CN']) - epp_errors << { - msg: 'Authentication error; server closing connection (certificate is not valid)', - code: '2501' - } + msg = 'Authentication error; server closing connection (certificate is not valid)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end end if success && !@api_user - epp_errors << { - msg: 'Authentication error; server closing connection (API user not found)', - code: '2501' - } + msg = 'Authentication error; server closing connection (API user not found)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end if success && !@api_user.try(:active) - epp_errors << { - msg: 'Authentication error; server closing connection (API user is not active)', - code: '2501' - } + msg = 'Authentication error; server closing connection (API user is not active)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end if success && @api_user.cannot?(:create, :epp_login) - epp_errors << { - msg: 'Authentication error; server closing connection (API user does not have epp role)', - code: '2501' - } + msg = 'Authentication error; server closing connection (API user does not have epp role)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end if success && !ip_white? - epp_errors << { - msg: 'Authentication error; server closing connection (IP is not whitelisted)', - code: '2501' - } + msg = 'Authentication error; server closing connection (IP is not whitelisted)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2501') success = false end if success && EppSession.limit_reached?(@api_user.registrar) - epp_errors << { - msg: 'Session limit exceeded; server closing connection (connection limit reached)', - code: '2502', - } + msg = 'Session limit exceeded; server closing connection (connection limit reached)' + epp_errors.add(:epp_errors, + msg: msg, + code: '2502') success = false end @@ -98,10 +98,9 @@ module Epp already_authenticated = EppSession.exists?(session_id: epp_session_id) if already_authenticated - epp_errors << { - msg: 'Command use error; Already authenticated', - code: 2002, - } + epp_errors.add(:epp_errors, + msg: 'Command use error; Already authenticated', + code: 2002) handle_errors return end @@ -127,10 +126,9 @@ module Epp def logout unless signed_in? - epp_errors << { - code: 2201, - msg: 'Authorization error' - } + epp_errors.add(:epp_errors, + code: 2201, + msg: 'Authorization error') handle_errors return end diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 74bbf89e8..b1609a863 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -5,7 +5,7 @@ class Registrar def new authorize! :manage, :repp @expire_date = Time.zone.now.to_date - render file: 'registrar/bulk_change/new', locals: { active_tab: default_tab } + render 'registrar/bulk_change/new', locals: { active_tab: default_tab } end def bulk_renew @@ -21,7 +21,7 @@ class Registrar flash[:notice] = nil end - render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew } + render 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew } end private @@ -45,7 +45,7 @@ class Registrar redirect_to registrar_domains_url else @error = response.code == '404' ? 'Contact(s) not found' : parsed_response[:message] - render file: 'registrar/bulk_change/new', locals: { active_tab: active_tab } + render 'registrar/bulk_change/new', locals: { active_tab: active_tab } end end diff --git a/app/controllers/registrar/contacts_controller.rb b/app/controllers/registrar/contacts_controller.rb index 18af3a29f..756495457 100644 --- a/app/controllers/registrar/contacts_controller.rb +++ b/app/controllers/registrar/contacts_controller.rb @@ -43,9 +43,11 @@ class Registrar send_data raw_csv, filename: 'contacts.csv', type: "#{Mime[:csv]}; charset=utf-8" end format.pdf do - view = ActionView::Base.new(ActionController::Base.view_paths, contacts: contacts) - view.class_eval { include ::ApplicationHelper } - raw_html = view.render(file: 'registrar/contacts/list_pdf', layout: false) + raw_html = ApplicationController.render( + template: 'registrar/contacts/list_pdf', + assigns: { contacts: contacts }, + formats: [:html] + ) raw_pdf = contacts.pdf(raw_html) send_data raw_pdf, filename: 'contacts.pdf' diff --git a/app/controllers/registrar/domain_transfers_controller.rb b/app/controllers/registrar/domain_transfers_controller.rb index e055c38d8..64e3910ec 100644 --- a/app/controllers/registrar/domain_transfers_controller.rb +++ b/app/controllers/registrar/domain_transfers_controller.rb @@ -36,7 +36,7 @@ class Registrar redirect_to registrar_domains_url else @api_errors = parsed_response[:message] - render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_transfer } + render 'registrar/bulk_change/new', locals: { active_tab: :bulk_transfer } end else params[:request] = true # EPP domain:transfer "op" attribute diff --git a/app/controllers/registrar/nameservers_controller.rb b/app/controllers/registrar/nameservers_controller.rb index 3eb23cd48..852a5e796 100644 --- a/app/controllers/registrar/nameservers_controller.rb +++ b/app/controllers/registrar/nameservers_controller.rb @@ -27,7 +27,7 @@ class Registrar flash: { notice: compose_notice_message(parsed_response) }) else @api_errors = parsed_response[:message] - render file: 'registrar/bulk_change/new', locals: { active_tab: :nameserver } + render 'registrar/bulk_change/new', locals: { active_tab: :nameserver } end end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 53519195c..37d4b95be 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -17,10 +17,10 @@ module Repp @response = { code: 2303, message: 'Object does not exist' } render(json: @response, status: :not_found) rescue ActionController::ParameterMissing, Apipie::ParamMissing => e - @response = { code: 2003, message: e } + @response = { code: 2003, message: e.message.gsub(/\n/, '. ') } render(json: @response, status: :bad_request) rescue Apipie::ParamInvalid => e - @response = { code: 2005, message: e } + @response = { code: 2005, message: e.message.gsub(/\n/, '. ') } render(json: @response, status: :bad_request) ensure create_repp_log @@ -60,40 +60,28 @@ module Repp end def epp_errors - @epp_errors ||= [] + @epp_errors ||= ActiveModel::Errors.new(self) end - def handle_errors(obj = nil, update: false) - @epp_errors ||= [] + def handle_errors(obj = nil) + @epp_errors ||= ActiveModel::Errors.new(self) - obj&.construct_epp_errors - @epp_errors += obj.errors[:epp_errors] if obj - - format_epp_errors if update - @epp_errors.uniq! + if obj + obj.construct_epp_errors + obj.errors.each { |error| @epp_errors.import error } + end render_epp_error end - def format_epp_errors - @epp_errors.each_with_index do |error, index| - blocked_by_delete_prohibited?(error, index) - end - end - - def blocked_by_delete_prohibited?(error, index) - if error[:code] == 2304 && error[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - error[:value][:obj] == 'status' - - @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE - end - end - def render_epp_error(status = :bad_request, data = {}) - @epp_errors ||= [] - @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} + @epp_errors ||= ActiveModel::Errors.new(self) + @epp_errors.add(:epp_errors, msg: 'Command failed', code: '2304') if data != {} - @response = { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data } + error_options = @epp_errors.errors.uniq + .select { |error| error.options[:code].present? }[0].options + + @response = { code: error_options[:code].to_i, message: error_options[:msg], data: data } render(json: @response, status: status) end diff --git a/app/controllers/repp/v1/domains/admin_contacts_controller.rb b/app/controllers/repp/v1/domains/admin_contacts_controller.rb index 2e9a285eb..6ec0e129b 100644 --- a/app/controllers/repp/v1/domains/admin_contacts_controller.rb +++ b/app/controllers/repp/v1/domains/admin_contacts_controller.rb @@ -6,7 +6,9 @@ module Repp super unless @new_contact.identical_to?(@current_contact) - @epp_errors << { code: 2304, msg: 'Admin contacts must be identical' } + @epp_errors.add(:epp_errors, + msg: 'Admin contacts must be identical', + code: '2304') end return handle_errors if @epp_errors.any? diff --git a/app/controllers/repp/v1/domains/base_contacts_controller.rb b/app/controllers/repp/v1/domains/base_contacts_controller.rb index b601c5313..65dbea9ac 100644 --- a/app/controllers/repp/v1/domains/base_contacts_controller.rb +++ b/app/controllers/repp/v1/domains/base_contacts_controller.rb @@ -15,8 +15,12 @@ module Repp end def update - @epp_errors ||= [] - @epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid? + @epp_errors ||= ActiveModel::Errors.new(self) + return unless @new_contact.invalid? + + @epp_errors.add(:epp_errors, + msg: 'New contact must be valid', + code: '2304') end private diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 3bde107d1..4c89243c7 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -50,7 +50,9 @@ module Repp super if @new_contact == @current_contact - @epp_errors << { code: 2304, msg: 'New contact must be different from current' } + @epp_errors.add(:epp_errors, + msg: 'New contact must be different from current', + code: '2304') end return handle_errors if @epp_errors.any? diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index c356d799a..af40e17b1 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -29,8 +29,8 @@ module Repp renew = run_bulk_renew_task(@domains, bulk_renew_params[:renew_period]) return render_success(data: { updated_domains: @domains.map(&:name) }) if renew.valid? - @epp_errors << { code: 2002, - msg: renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ') } + msg = renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ') + @epp_errors.add(:epp_errors, msg: msg, code: '2002') handle_errors end @@ -41,20 +41,20 @@ module Repp end def validate_renew_period - @epp_errors ||= [] + @epp_errors ||= ActiveModel::Errors.new(self) periods = Depp::Domain::PERIODS.map { |p| p[1] } return if periods.include? bulk_renew_params[:renew_period] - @epp_errors << { code: 2005, msg: 'Invalid renew period' } + @epp_errors.add(:epp_errors, msg: 'Invalid renew period', code: '2005') end def select_renewable_domains - @epp_errors ||= [] + @epp_errors ||= ActiveModel::Errors.new(self) if bulk_renew_params[:domains].instance_of?(Array) @domains = bulk_renew_domains else - @epp_errors << { code: 2005, msg: 'Domains attribute must be an array' } + @epp_errors.add(:epp_errors, msg: 'Domains attribute must be an array', code: '2005') end return handle_errors if @epp_errors.any? @@ -73,12 +73,16 @@ module Repp end def bulk_renew_domains - @epp_errors ||= [] + @epp_errors ||= ActiveModel::Errors.new(self) domains = [] bulk_renew_params[:domains].each do |idn| domain = Epp::Domain.find_by(name: idn) domains << domain if domain - @epp_errors << { code: 2304, msg: "Object does not exist: #{idn}" } unless domain + next if domain + + @epp_errors.add(:epp_errors, + msg: "Object does not exist: #{idn}", + code: '2304') end domains diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index b058f4505..c228bd328 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -114,7 +114,6 @@ module Repp transfer_params[:domain_transfers].each do |transfer| initiate_transfer(transfer) end - render_success(data: { success: @successful, failed: @errors }) end @@ -151,7 +150,7 @@ module Repp @successful << { type: 'domain_transfer', domain_name: domain.name } else @errors << { type: 'domain_transfer', domain_name: domain.name, - errors: domain.errors[:epp_errors] } + errors: domain.errors.where(:epp_errors).first.options } end end @@ -187,7 +186,7 @@ module Repp end def set_authorized_domain - @epp_errors ||= [] + @epp_errors ||= ActiveModel::Errors.new(self) @domain = domain_from_url_hash end @@ -195,7 +194,9 @@ module Repp return if @domain.registrar == current_user.registrar return if @domain.transfer_code.eql?(request.headers['Auth-Code']) - @epp_errors << { code: 2202, msg: I18n.t('errors.messages.epp_authorization_error') } + @epp_errors.add(:epp_errors, + code: 2202, + msg: I18n.t('errors.messages.epp_authorization_error')) handle_errors end diff --git a/app/controllers/repp/v1/registrar/notifications_controller.rb b/app/controllers/repp/v1/registrar/notifications_controller.rb index 4e00a9321..1185a1a73 100644 --- a/app/controllers/repp/v1/registrar/notifications_controller.rb +++ b/app/controllers/repp/v1/registrar/notifications_controller.rb @@ -18,6 +18,24 @@ module Repp render_success(data: data) end + api :GET, '/repp/v1/registrar/notifications/all_notifications' + desc 'Get the all unread poll messages' + def all_notifications + records = current_user.unread_notifications.order('created_at DESC').all + + @notification = records.limit(limit).offset(offset) + # rubocop:disable Style/AndOr + render_success(data: nil) and return unless @notification + # rubocop:enable Style/AndOr + + data = @notification.as_json(only: %i[id text attached_obj_id attached_obj_type]) + + message = 'Command completed successfully.'\ + " Returning #{@notification.count} out of #{records.count}."\ + ' Use URL parameters :limit and :offset to list other messages if needed.' + render_success(data: data, message: message) + end + api :GET, '/repp/v1/registrar/notifications/:notification_id' desc 'Get a specific poll message' def show @@ -45,6 +63,18 @@ module Repp def set_notification @notification = current_user.unread_notifications.find(params[:id]) end + + def limit + index_params[:limit] || 200 + end + + def offset + index_params[:offset] || 0 + end + + def index_params + params.permit(:limit, :offset) + end end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 5c742afce..7bf5cd110 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -62,7 +62,7 @@ module ApplicationHelper if model.updator.kind_of?(RegistrantUser) model.updator else - link_to(model.updator, ['admin', model.updator]) + link_to(model.updator, [:admin, model.updator]) end end diff --git a/app/helpers/sorted_country_helper.rb b/app/helpers/sorted_country_helper.rb new file mode 100644 index 000000000..af08210be --- /dev/null +++ b/app/helpers/sorted_country_helper.rb @@ -0,0 +1,32 @@ +module SortedCountryHelper + def all_country_options(selected = nil) + quick_options = options_for_select(quick_list, selected: selected) + + # no double select + selected = quick_list.map(&:second).include?(selected) ? '' : selected + + all_options = options_for_select([['---', '---']] + all_sorted_truncated, + selected: selected, disabled: ['---']) + quick_options + all_options + end + + def quick_list + [ + %w[Estonia EE], + %w[Finland FI], + %w[Latvia LV], + %w[Lithuania LT], + ['Russian Federation', 'RU'], + %w[Sweden SE], + ['United States', 'US'], + ] + end + + def all_sorted + Country.all.sort_by(&:name) + end + + def all_sorted_truncated + all_sorted.map { |country| [country.name.truncate(26), country.alpha2] } + end +end diff --git a/app/interactions/actions/domain_transfer.rb b/app/interactions/actions/domain_transfer.rb index 1ff9aafe9..4da078d78 100644 --- a/app/interactions/actions/domain_transfer.rb +++ b/app/interactions/actions/domain_transfer.rb @@ -13,8 +13,8 @@ module Actions end def call - return unless domain_exists? - return unless valid_transfer_code? + return false unless domain_exists? + return false unless valid_transfer_code? run_validations diff --git a/app/interactions/domains/bulk_renew/start.rb b/app/interactions/domains/bulk_renew/start.rb index f758b52cd..88ad6a83d 100644 --- a/app/interactions/domains/bulk_renew/start.rb +++ b/app/interactions/domains/bulk_renew/start.rb @@ -41,7 +41,7 @@ module Domains end def manage_errors(task) - task.errors.each { |k, v| errors.add(k, v) } unless task.valid? + task.errors.each { |task_error| errors.import task_error } unless task.valid? errors.add(:domain, I18n.t('not_enough_funds')) unless task.result end diff --git a/app/interactions/domains/check_balance/mass.rb b/app/interactions/domains/check_balance/mass.rb index 58af25a9f..cf1867a9f 100644 --- a/app/interactions/domains/check_balance/mass.rb +++ b/app/interactions/domains/check_balance/mass.rb @@ -20,12 +20,14 @@ module Domains def calculate_total_price @total_price = 0 domains.each do |domain| - task = Domains::CheckBalance::SingleDomain.run(domain: domain, - operation: 'renew', - period: period, - unit: unit) + task = Domains::CheckBalance::SingleDomain.run(domain: domain, operation: 'renew', + period: period, unit: unit) - task.valid? ? @total_price += task.result : errors.merge!(task.errors) + if task.valid? + @total_price += task.result + else + task.errors.each { |task_error| errors.import task_error } + end end end end diff --git a/app/interactions/domains/update_confirm/process_update_confirmed.rb b/app/interactions/domains/update_confirm/process_update_confirmed.rb index 316c1db86..734dbf5d8 100644 --- a/app/interactions/domains/update_confirm/process_update_confirmed.rb +++ b/app/interactions/domains/update_confirm/process_update_confirmed.rb @@ -29,11 +29,17 @@ module Domains end def assign_domain_update_meta - user = ApiUser.find_by(id: domain.pending_json['current_user_id']) + user = ApiUser.find_by(id: user_id) if user_id.present? && user_id.is_a?(String) domain.upid = user.registrar.id if user.present? && user.registrar domain.up_date = Time.zone.now end + + private + + def user_id + @user_id ||= domain.pending_json.dig('current_user_id') + end end end end diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 24bf51e0c..734075ac3 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -1,5 +1,6 @@ class BankTransaction < ApplicationRecord include Versions + include TransactionPaidInvoices belongs_to :bank_statement has_one :account_activity @@ -17,16 +18,6 @@ class BankTransaction < ApplicationRecord account_activity.invoice end - def invoice - return unless registrar - - @invoice ||= registrar.invoices - .order(created_at: :asc) - .unpaid - .non_cancelled - .find_by(total: sum) - end - def registrar @registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer end diff --git a/app/models/billing/price.rb b/app/models/billing/price.rb index 9cd32f55c..cc42d643b 100644 --- a/app/models/billing/price.rb +++ b/app/models/billing/price.rb @@ -1,5 +1,6 @@ module Billing class Price < ApplicationRecord + attribute :duration, :interval include Billing::Price::Expirable include Versions @@ -8,33 +9,37 @@ module Billing validates :price, :valid_from, :operation_category, :duration, presence: true validates :operation_category, inclusion: { in: Proc.new { |price| price.class.operation_categories } } - validates :duration, inclusion: { in: Proc.new { |price| price.class.durations } } + validates :duration, inclusion: { in: Proc.new { |price| price.class.durations.values } }, if: :should_validate_duration? alias_attribute :effect_time, :valid_from alias_attribute :expire_time, :valid_to monetize :price_cents, allow_nil: true, numericality: { greater_than_or_equal_to: 0 } after_initialize :init_values + def should_validate_duration? + new_record? || duration_changed? + end + def self.operation_categories %w[create renew] end def self.durations - [ - '3 mons', - '6 mons', - '9 mons', - '1 year', - '2 years', - '3 years', - '4 years', - '5 years', - '6 years', - '7 years', - '8 years', - '9 years', - '10 years', - ] + { + '3 months' => 3.months, + '6 months' => 6.months, + '9 months' => 9.months, + '1 year' => 1.year, + '2 years' => 2.years, + '3 years' => 3.years, + '4 years' => 4.years, + '5 years' => 5.years, + '6 years' => 6.years, + '7 years' => 7.years, + '8 years' => 8.years, + '9 years' => 9.years, + '10 years' => 10.years, + } end def self.statuses diff --git a/app/models/concerns/epp_errors.rb b/app/models/concerns/epp_errors.rb index d12080158..90d742609 100644 --- a/app/models/concerns/epp_errors.rb +++ b/app/models/concerns/epp_errors.rb @@ -1,38 +1,45 @@ module EppErrors extend ActiveSupport::Concern + included do + attr_accessor :epp_errors + end def construct_epp_errors - epp_errors = [] - errors.messages.each do |attr, errors| - attr = attr.to_s.split('.')[0].to_sym + epp_errors = ActiveModel::Errors.new(self) + errors.each do |error| + attr = error.attribute.to_s.split('.')[0].to_sym next if attr == :epp_errors if self.class.reflect_on_association(attr) - epp_errors << collect_child_errors(attr) + collect_child_errors(attr).each do |child_error| + epp_errors.import child_error + end end if self.class.reflect_on_aggregation(attr) aggregation = send(attr) - epp_errors << collect_aggregation_errors(aggregation) + collect_aggregation_errors(aggregation).each do |aggregation_error| + epp_errors.import aggregation_error + end next end - - epp_errors << collect_parent_errors(attr, errors) + collect_parent_errors(attr, error.message).each do |parent_error| + epp_errors.import parent_error + end end - - errors.add(:epp_errors, epp_errors) - errors[:epp_errors].flatten! + epp_errors.each { |epp_error| errors.import epp_error } + errors end def collect_parent_errors(attr, errors) errors = [errors] if errors.is_a?(String) - epp_errors = [] + epp_errors = ActiveModel::Errors.new(self) errors.each do |err| code, value = find_epp_code_and_value(err) next unless code msg = attr.to_sym == :base ? err : "#{err} [#{attr}]" - epp_errors << { code: code, msg: msg, value: value } + epp_errors.add(attr, code: code, msg: msg, value: value) end epp_errors end @@ -40,20 +47,24 @@ module EppErrors 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(attr).each do |x| - x.errors.messages.each do |attribute, errors| - epp_errors << x.collect_parent_errors(attribute, errors) + epp_errors = ActiveModel::Errors.new(self) + + if multi.include?(macro) + send(attr).each do |x| + x.errors.each do |error| + x.collect_parent_errors(error.attribute, error.message).each do |parent_error| + epp_errors.import parent_error + end + end end - end if multi.include?(macro) + end epp_errors end def collect_aggregation_errors(aggregation) - epp_errors = [] + epp_errors = ActiveModel::Errors.new(self) aggregation.errors.details.each do |attr, error_details| error_details.each do |error_detail| @@ -69,7 +80,7 @@ module EppErrors message = "#{aggregation.model_name.human} #{message.camelize(:lower)}" end - epp_errors << { code: epp_code, msg: message } + epp_errors.add(attr, code: epp_code, msg: message) end end end @@ -105,11 +116,20 @@ module EppErrors end def add_epp_error(code, obj, val, msg) - errors[:epp_errors] ||= [] t = errors.generate_message(*msg) if msg.is_a?(Array) t = msg if msg.is_a?(String) err = { code: code, msg: t } + val = check_for_status(code, obj, val) err[:value] = { val: val, obj: obj } if val.present? - errors[:epp_errors] << err + self.errors.add(:epp_errors, err) + end + + def check_for_status(code, obj, val) + if code == '2304' && val.present? && val == DomainStatus::SERVER_DELETE_PROHIBITED && + obj == 'status' + DomainStatus::PENDING_UPDATE + else + val + end end end diff --git a/app/models/concerns/registrar/book_keeping.rb b/app/models/concerns/registrar/book_keeping.rb index d8a7c2e72..710f48e3c 100644 --- a/app/models/concerns/registrar/book_keeping.rb +++ b/app/models/concerns/registrar/book_keeping.rb @@ -40,8 +40,8 @@ module Registrar::BookKeeping def fetch_invoice_lines(activity, lines) price = load_price(activity) - if price.duration.include? 'year' - price.duration.to_i.times do |duration| + if price.duration.in_years.to_i >= 1 + price.duration.in_years.to_i.times do |duration| lines << new_monthly_invoice_line(activity: activity, duration: duration + 1).as_json end else @@ -67,9 +67,9 @@ module Registrar::BookKeeping end def finalize_invoice_line(line, price:, activity:, duration:) - yearly = price.duration.include?('year') + yearly = price.duration.in_years.to_i >= 1 - line['price'] = yearly ? (price.price.amount / price.duration.to_i) : price.price.amount + line['price'] = yearly ? (price.price.amount / price.duration.in_years.to_i) : price.price.amount line['description'] = description_in_language(price: price, yearly: yearly) if duration.present? @@ -90,7 +90,7 @@ module Registrar::BookKeeping locale_string = "registrar.invoice_#{timeframe_string}_product_description" I18n.with_locale(language == 'en' ? 'en' : 'et') do - I18n.t(locale_string, tld: ".#{price.zone_name}", length: price.duration.to_i) + I18n.t(locale_string, tld: ".#{price.zone_name}", length: price.duration.in_years.to_i) end end diff --git a/app/models/concerns/transaction_paid_invoices.rb b/app/models/concerns/transaction_paid_invoices.rb new file mode 100644 index 000000000..19d632c1d --- /dev/null +++ b/app/models/concerns/transaction_paid_invoices.rb @@ -0,0 +1,37 @@ +module TransactionPaidInvoices + extend ActiveSupport::Concern + + def invoice + return unless registrar + + @invoice ||= registrar.invoices + .order(created_at: :asc) + .unpaid + .non_cancelled + .find_by(total: sum) + end + + def non_canceled? + paid_invoices = registrar.invoices + .order(created_at: :asc) + .non_cancelled + .where(total: sum) + paid_invoices.any? do |invoice| + return true if invoice.paid? && fresh_admin_paid_invoice(invoice) + end + end + + private + + def fresh_admin_paid_invoice(invoice) + check_for_date_paid_invoice(invoice) && does_invoice_created_by_admin?(invoice) + end + + def check_for_date_paid_invoice(invoice) + invoice.account_activity.created_at > Time.zone.today - 2.days + end + + def does_invoice_created_by_admin?(invoice) + invoice.account_activity.creator_str&.include? 'Admin' + end +end diff --git a/app/models/dnskey.rb b/app/models/dnskey.rb index 58d4d9e31..e9e15802f 100644 --- a/app/models/dnskey.rb +++ b/app/models/dnskey.rb @@ -60,19 +60,19 @@ class Dnskey < ApplicationRecord def validate_algorithm return if alg.blank? return if ALGORITHMS.include?(alg.to_s) - errors.add(:alg, :invalid, values: ALGORITHMS.join(', ')) + errors.add(:alg, :invalid, values: "Valid algorithms are: #{ALGORITHMS.join(', ')}") end def validate_protocol return if protocol.blank? return if PROTOCOLS.include?(protocol.to_s) - errors.add(:protocol, :invalid, values: PROTOCOLS.join(', ')) + errors.add(:protocol, :invalid, values: "Valid protocols are: #{PROTOCOLS.join(', ')}") end def validate_flags return if flags.blank? return if FLAGS.include?(flags.to_s) - errors.add(:flags, :invalid, values: FLAGS.join(', ')) + errors.add(:flags, :invalid, values: "Valid flags are: #{FLAGS.join(', ')}") end def generate_digest diff --git a/app/models/invoice/pdf_generator.rb b/app/models/invoice/pdf_generator.rb index 2078fad20..14fb99814 100644 --- a/app/models/invoice/pdf_generator.rb +++ b/app/models/invoice/pdf_generator.rb @@ -14,9 +14,7 @@ class Invoice private def invoice_html - view = ActionView::Base.new(ActionController::Base.view_paths, invoice: invoice) - view.class_eval { include ApplicationHelper } - view.render(file: 'invoice/pdf', layout: false) + ApplicationController.render(template: 'invoice/pdf', assigns: { invoice: invoice }) end end -end \ No newline at end of file +end diff --git a/app/models/legal_document.rb b/app/models/legal_document.rb index 377a9fdde..07c50c5dc 100644 --- a/app/models/legal_document.rb +++ b/app/models/legal_document.rb @@ -1,6 +1,7 @@ class LegalDocument < ApplicationRecord include EppErrors MIN_BODY_SIZE = (1.37 * 3.kilobytes).ceil + MAX_BODY_SIZE = 8.megabytes if ENV['legal_document_types'].present? TYPES = ENV['legal_document_types'].split(',').map(&:strip) @@ -20,14 +21,19 @@ class LegalDocument < ApplicationRecord def epp_code_map { - '2306' => [ - [:body, :length] - ] + '2308' => [ + %i[body length_more_than], + %i[body length_less_than], + ] } end def val_body_length - errors.add(:body, :length) if body.nil? || body.size < MIN_BODY_SIZE + if body.nil? || body.size < MIN_BODY_SIZE + errors.add(:body, :length_more_than) + elsif body.size > MAX_BODY_SIZE + errors.add(:body, :length_less_than) + end end def save_to_filesystem diff --git a/app/models/sorted_country.rb b/app/models/sorted_country.rb deleted file mode 100644 index 7b27adacb..000000000 --- a/app/models/sorted_country.rb +++ /dev/null @@ -1,40 +0,0 @@ -class SortedCountry - class << self - include ActionView::Helpers - - def all_options(selected = nil) - quick_options = options_for_select(quick_list, selected: selected) - - # no double select - selected = quick_list.map(&:second).include?(selected) ? '' : selected - - all_options = options_for_select([['---', '---']] + all_sorted_truncated, - selected: selected, disabled: ['---']) - quick_options + all_options - end - - private - - def quick_list - @quick_list ||= - [ - %w[Estonia EE], - %w[Finland FI], - %w[Latvia LV], - %w[Lithuania LT], - ['Russian Federation', 'RU'], - %w[Sweden SE], - ['United States', 'US'] - ] - end - - def all_sorted - @all_sorted ||= Country.all.sort_by(&:name) - end - - def all_sorted_truncated - @all_sorted_truncated ||= - all_sorted.map { |country| [country.name.truncate(26), country.alpha2] } - end - end -end diff --git a/app/views/admin/admin_users/_form.haml b/app/views/admin/admin_users/_form.haml index ddee458ba..d23ff40cb 100644 --- a/app/views/admin/admin_users/_form.haml +++ b/app/views/admin/admin_users/_form.haml @@ -35,14 +35,14 @@ .col-md-4.control-label = f.label :country_code, t(:country), class: 'required' .col-md-8 - = f.select :country_code, SortedCountry.all_options(f.object.country_code), {}, required: true, class: 'form-control' + = f.select :country_code, ApplicationController.helpers.all_country_options(f.object.country_code), {}, required: true, class: 'form-control' %hr .form-group .col-md-4.control-label = f.label :role, nil, class: 'required' .col-md-8 - = select_tag 'admin_user[roles][]', - options_for_select(AdminUser::ROLES.map {|x| [t(x), x] }, + = select_tag 'admin_user[roles][]', + options_for_select(AdminUser::ROLES.map {|x| [t(x), x] }, @admin_user.roles.try(:first)), class: 'form-control selectize' %hr diff --git a/app/views/admin/billing/prices/_price.html.erb b/app/views/admin/billing/prices/_price.html.erb index 199f53e81..b9f81f8d4 100644 --- a/app/views/admin/billing/prices/_price.html.erb +++ b/app/views/admin/billing/prices/_price.html.erb @@ -1,6 +1,6 @@