From 0f3b033f790636c0209a1def1fb45665289a8084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 16 Sep 2020 15:59:02 +0300 Subject: [PATCH 001/202] Create bounced_mail_addresses table --- ...916125326_create_bounced_mail_addresses.rb | 12 ++++ db/structure.sql | 65 +++++++++++++++++-- 2 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20200916125326_create_bounced_mail_addresses.rb diff --git a/db/migrate/20200916125326_create_bounced_mail_addresses.rb b/db/migrate/20200916125326_create_bounced_mail_addresses.rb new file mode 100644 index 000000000..c6600afea --- /dev/null +++ b/db/migrate/20200916125326_create_bounced_mail_addresses.rb @@ -0,0 +1,12 @@ +class CreateBouncedMailAddresses < ActiveRecord::Migration[6.0] + def change + create_table :bounced_mail_addresses do |t| + t.string :email, null: false + t.string :bounce_reason, null: false + t.integer :incidents, null: false, default: 1 + t.jsonb :response_json + + t.timestamps + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 6a30fbc84..3ec055386 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -475,6 +475,40 @@ CREATE SEQUENCE public.blocked_domains_id_seq ALTER SEQUENCE public.blocked_domains_id_seq OWNED BY public.blocked_domains.id; +-- +-- Name: bounced_mail_addresses; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE public.bounced_mail_addresses ( + id bigint NOT NULL, + email character varying NOT NULL, + bounce_reason character varying NOT NULL, + incidents integer DEFAULT 1 NOT NULL, + response_json jsonb, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: bounced_mail_addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.bounced_mail_addresses_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: bounced_mail_addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.bounced_mail_addresses_id_seq OWNED BY public.bounced_mail_addresses.id; + + -- -- Name: certificates; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -1706,7 +1740,7 @@ ALTER SEQUENCE public.log_payment_orders_id_seq OWNED BY public.log_payment_orde -- --- Name: log_prices; Type: TABLE; Schema: public; Owner: - +-- Name: log_prices; Type: TABLE; Schema: public; Owner: -; Tablespace: -- CREATE TABLE public.log_prices ( @@ -1744,7 +1778,7 @@ ALTER SEQUENCE public.log_prices_id_seq OWNED BY public.log_prices.id; -- --- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: - +-- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace: -- CREATE TABLE public.log_registrant_verifications ( @@ -2658,6 +2692,13 @@ ALTER TABLE ONLY public.bank_transactions ALTER COLUMN id SET DEFAULT nextval('p ALTER TABLE ONLY public.blocked_domains ALTER COLUMN id SET DEFAULT nextval('public.blocked_domains_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.bounced_mail_addresses ALTER COLUMN id SET DEFAULT nextval('public.bounced_mail_addresses_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2876,14 +2917,14 @@ ALTER TABLE ONLY public.log_payment_orders ALTER COLUMN id SET DEFAULT nextval(' -- --- Name: log_prices id; Type: DEFAULT; Schema: public; Owner: - +-- Name: id; Type: DEFAULT; Schema: public; Owner: - -- ALTER TABLE ONLY public.log_prices ALTER COLUMN id SET DEFAULT nextval('public.log_prices_id_seq'::regclass); -- --- Name: log_registrant_verifications id; Type: DEFAULT; Schema: public; Owner: - +-- Name: id; Type: DEFAULT; Schema: public; Owner: - -- ALTER TABLE ONLY public.log_registrant_verifications ALTER COLUMN id SET DEFAULT nextval('public.log_registrant_verifications_id_seq'::regclass); @@ -3100,6 +3141,14 @@ ALTER TABLE ONLY public.blocked_domains ADD CONSTRAINT blocked_domains_pkey PRIMARY KEY (id); +-- +-- Name: bounced_mail_addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY public.bounced_mail_addresses + ADD CONSTRAINT bounced_mail_addresses_pkey PRIMARY KEY (id); + + -- -- Name: certificates_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -3349,7 +3398,7 @@ ALTER TABLE ONLY public.log_payment_orders -- --- Name: log_prices log_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: log_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- ALTER TABLE ONLY public.log_prices @@ -3357,7 +3406,7 @@ ALTER TABLE ONLY public.log_prices -- --- Name: log_registrant_verifications log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- ALTER TABLE ONLY public.log_registrant_verifications @@ -4906,5 +4955,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200902131603'), ('20200908131554'), ('20200910085157'), -('20200910102028'); +('20200910102028'), +('20200916125326'); + From 188cca0c063325b91d641e808fbd3592a38f66eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 11:12:01 +0300 Subject: [PATCH 002/202] Create views for bounced emails --- .../bounced_mail_addresses_controller.rb | 60 +++++++++++++++++++ app/models/ability.rb | 1 + app/models/bounced_mail_address.rb | 2 + .../bounced_mail_addresses/_form.html.erb | 37 ++++++++++++ .../bounced_mail_addresses/edit.html.erb | 6 ++ .../bounced_mail_addresses/index.html.erb | 33 ++++++++++ .../admin/bounced_mail_addresses/new.html.erb | 5 ++ .../bounced_mail_addresses/show.html.erb | 24 ++++++++ config/routes.rb | 1 + 9 files changed, 169 insertions(+) create mode 100644 app/controllers/admin/bounced_mail_addresses_controller.rb create mode 100644 app/models/bounced_mail_address.rb create mode 100644 app/views/admin/bounced_mail_addresses/_form.html.erb create mode 100644 app/views/admin/bounced_mail_addresses/edit.html.erb create mode 100644 app/views/admin/bounced_mail_addresses/index.html.erb create mode 100644 app/views/admin/bounced_mail_addresses/new.html.erb create mode 100644 app/views/admin/bounced_mail_addresses/show.html.erb diff --git a/app/controllers/admin/bounced_mail_addresses_controller.rb b/app/controllers/admin/bounced_mail_addresses_controller.rb new file mode 100644 index 000000000..a33f90ab3 --- /dev/null +++ b/app/controllers/admin/bounced_mail_addresses_controller.rb @@ -0,0 +1,60 @@ +module Admin + class BouncedMailAddressesController < BaseController + before_action :set_bounced_mail_address, only: %i[show edit update destroy] + load_and_authorize_resource + + # GET /bounced_mail_addresses + def index + @bounced_mail_addresses = BouncedMailAddress.all + end + + # GET /bounced_mail_addresses/1 + def show; end + + # GET /bounced_mail_addresses/new + def new + @bounced_mail_address = BouncedMailAddress.new + end + + # GET /bounced_mail_addresses/1/edit + def edit; end + + # POST /bounced_mail_addresses + def create + @bounced_mail_address = BouncedMailAddress.new(bounced_mail_address_params) + + if @bounced_mail_address.save + redirect_to(admin_bounced_mail_addresses_url, notice: 'Bounced mail address was successfully created.') + else + render(:new) + end + end + + # PATCH/PUT /bounced_mail_addresses/1 + def update + if @bounced_mail_address.update(bounced_mail_address_params) + redirect_to(@bounced_mail_address, notice: 'Bounced mail address was successfully updated.') + else + render(:edit) + end + end + + # DELETE /bounced_mail_addresses/1 + def destroy + @bounced_mail_address.destroy + redirect_to(admin_bounced_mail_addresses_url, notice: 'Bounced mail address was successfully destroyed.') + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_bounced_mail_address + @bounced_mail_address = BouncedMailAddress.find(params[:id]) + end + + # Only allow a trusted parameter "white list" through. + def bounced_mail_address_params + params.require(:bounced_mail_address).permit(:email, :bounce_reason, :incidents, :response_json) + end + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index dce8a515b..31637b8ea 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -109,6 +109,7 @@ class Ability can :destroy, :pending can :create, :zonefile can :access, :settings_menu + can :manage, BouncedMailAddress end def static_registrant diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb new file mode 100644 index 000000000..d8f6a037b --- /dev/null +++ b/app/models/bounced_mail_address.rb @@ -0,0 +1,2 @@ +class BouncedMailAddress < ApplicationRecord +end diff --git a/app/views/admin/bounced_mail_addresses/_form.html.erb b/app/views/admin/bounced_mail_addresses/_form.html.erb new file mode 100644 index 000000000..7a384bd3f --- /dev/null +++ b/app/views/admin/bounced_mail_addresses/_form.html.erb @@ -0,0 +1,37 @@ +<%= form_for([:admin, @bounced_mail_address], html: { class: 'form-horizontal' }) do |form| %> + <% if @bounced_mail_address.errors.any? %> +
+

<%= pluralize(bounced_mail_address.errors.count, "error") %> prohibited this bounced_mail_address from being saved:

+ +
    + <% @bounced_mail_address.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= form.label :email %> + <%= form.text_field :email %> +
+ +
+ <%= form.label :bounce_reason %> + <%= form.text_field :bounce_reason %> +
+ +
+ <%= form.label :incidents %> + <%= form.number_field :incidents %> +
+ +
+ <%= form.label :response_json %> + <%= form.text_field :response_json %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/admin/bounced_mail_addresses/edit.html.erb b/app/views/admin/bounced_mail_addresses/edit.html.erb new file mode 100644 index 000000000..a3dfe2d84 --- /dev/null +++ b/app/views/admin/bounced_mail_addresses/edit.html.erb @@ -0,0 +1,6 @@ +

Editing Bounced Mail Address

+ +<%= render 'form', bounced_mail_address: @bounced_mail_address %> + +<%= link_to 'Show', admin_bounced_mail_address_path(@bounced_mail_address)%> | +<%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/app/views/admin/bounced_mail_addresses/index.html.erb b/app/views/admin/bounced_mail_addresses/index.html.erb new file mode 100644 index 000000000..c2f95ca68 --- /dev/null +++ b/app/views/admin/bounced_mail_addresses/index.html.erb @@ -0,0 +1,33 @@ +

<%= notice %>

+ +

Bounced Mail Addresses

+ + + + + + + + + + + + + + <% @bounced_mail_addresses.each do |mail_addr| %> + + + + + + + + + + <% end %> + +
EmailBounce reasonIncidentsResponse json
<%= mail_addr.email %><%= mail_addr.bounce_reason %><%= mail_addr.incidents %><%= mail_addr.response_json %><%= link_to 'Show', admin_bounced_mail_address_path(mail_addr) %><%= link_to 'Edit', edit_admin_bounced_mail_address_path(mail_addr) %><%= link_to 'Destroy', admin_bounced_mail_address_path(mail_addr), method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%= link_to 'New Bounced Mail Address', new_admin_bounced_mail_address_path %> diff --git a/app/views/admin/bounced_mail_addresses/new.html.erb b/app/views/admin/bounced_mail_addresses/new.html.erb new file mode 100644 index 000000000..010ac79dc --- /dev/null +++ b/app/views/admin/bounced_mail_addresses/new.html.erb @@ -0,0 +1,5 @@ +

New Bounced Mail Address

+ +<%= render 'form', bounced_mail_address: @bounced_mail_address %> + +<%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/app/views/admin/bounced_mail_addresses/show.html.erb b/app/views/admin/bounced_mail_addresses/show.html.erb new file mode 100644 index 000000000..1f48ad5cf --- /dev/null +++ b/app/views/admin/bounced_mail_addresses/show.html.erb @@ -0,0 +1,24 @@ +

<%= notice %>

+ +

+ Email: + <%= @bounced_mail_address.email %> +

+ +

+ Bounce reason: + <%= @bounced_mail_address.bounce_reason %> +

+ +

+ Incidents: + <%= @bounced_mail_address.incidents %> +

+ +

+ Response json: + <%= @bounced_mail_address.response_json %> +

+ +<%= link_to 'Edit', edit_admin_bounced_mail_address_path(@bounced_mail_address) %> | +<%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/config/routes.rb b/config/routes.rb index 223cf3171..cdbd63f31 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -298,6 +298,7 @@ Rails.application.routes.draw do resources :delayed_jobs resources :epp_logs resources :repp_logs + resources :bounced_mail_addresses authenticate :admin_user do mount Que::Web, at: 'que' From 22f9c2058d137a021034030c66ac33f8049f17a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 11:55:52 +0300 Subject: [PATCH 003/202] Create api/v1/bounces endpoint --- app/controllers/api/v1/bounces_controller.rb | 12 ++++++++++++ config/routes.rb | 1 + 2 files changed, 13 insertions(+) create mode 100644 app/controllers/api/v1/bounces_controller.rb diff --git a/app/controllers/api/v1/bounces_controller.rb b/app/controllers/api/v1/bounces_controller.rb new file mode 100644 index 000000000..cff8c3efe --- /dev/null +++ b/app/controllers/api/v1/bounces_controller.rb @@ -0,0 +1,12 @@ +module Api + module V1 + class BouncesController < BaseController + before_action :authenticate + + def create + bounced_mail_address = BouncedMailAddress.record(json) + bounced_mail_address ? render(head: :ok) : render(head: :failed) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index cdbd63f31..89f8f0cd6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -64,6 +64,7 @@ Rails.application.routes.draw do end resources :auctions, only: %i[index show update], param: :uuid + resources :bounces, only: %i[create] end match '*all', controller: 'cors', action: 'cors_preflight_check', via: [:options], From 140df0acf78a9e0139301649b244247c66b612e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 13:26:00 +0300 Subject: [PATCH 004/202] Fix CC issues --- .../admin/bounced_mail_addresses_controller.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/bounced_mail_addresses_controller.rb b/app/controllers/admin/bounced_mail_addresses_controller.rb index a33f90ab3..551413e2c 100644 --- a/app/controllers/admin/bounced_mail_addresses_controller.rb +++ b/app/controllers/admin/bounced_mail_addresses_controller.rb @@ -24,7 +24,10 @@ module Admin @bounced_mail_address = BouncedMailAddress.new(bounced_mail_address_params) if @bounced_mail_address.save - redirect_to(admin_bounced_mail_addresses_url, notice: 'Bounced mail address was successfully created.') + redirect_to( + admin_bounced_mail_addresses_url, + notice: 'Bounced mail address was successfully created.' + ) else render(:new) end @@ -42,7 +45,10 @@ module Admin # DELETE /bounced_mail_addresses/1 def destroy @bounced_mail_address.destroy - redirect_to(admin_bounced_mail_addresses_url, notice: 'Bounced mail address was successfully destroyed.') + redirect_to( + admin_bounced_mail_addresses_url, + notice: 'Bounced mail address was successfully destroyed.' + ) end private @@ -54,7 +60,12 @@ module Admin # Only allow a trusted parameter "white list" through. def bounced_mail_address_params - params.require(:bounced_mail_address).permit(:email, :bounce_reason, :incidents, :response_json) + params.require(:bounced_mail_address).permit( + :email, + :bounce_reason, + :incidents, + :response_json + ) end end end From 6af37a787dc56ec7b4d89079d6bf11d5ddf58651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 13:26:48 +0300 Subject: [PATCH 005/202] Restructure bounced mails view --- app/models/bounced_mail_address.rb | 22 +++++++ .../bounced_mail_addresses/index.html.erb | 61 ++++++++++--------- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index d8f6a037b..6609829da 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -1,2 +1,24 @@ class BouncedMailAddress < ApplicationRecord + validates :email, presence: true + validates :bounce_reason, presence: true + + def diagnostic + response_json['diagnosticCode'] + end + + def action + response_json['action'] + end + + def status + response_json['status'] + end + + def self.record(json) + bounced_records = json['bounce']['bouncedRecipients'] + bounced_records.each do |record| + bounce_record = BouncedMailAddress.new(email: record['emailAddress'], response_json: record) + bounce_record.save + end + end end diff --git a/app/views/admin/bounced_mail_addresses/index.html.erb b/app/views/admin/bounced_mail_addresses/index.html.erb index c2f95ca68..b6a019282 100644 --- a/app/views/admin/bounced_mail_addresses/index.html.erb +++ b/app/views/admin/bounced_mail_addresses/index.html.erb @@ -2,32 +2,37 @@

Bounced Mail Addresses

- - - - - - - - - - +
+
+
+
+
EmailBounce reasonIncidentsResponse json
+ + + + + + + + + + - - <% @bounced_mail_addresses.each do |mail_addr| %> - - - - - - - - - - <% end %> - -
EmailActionStatusDiagnosticTrackedActions
<%= mail_addr.email %><%= mail_addr.bounce_reason %><%= mail_addr.incidents %><%= mail_addr.response_json %><%= link_to 'Show', admin_bounced_mail_address_path(mail_addr) %><%= link_to 'Edit', edit_admin_bounced_mail_address_path(mail_addr) %><%= link_to 'Destroy', admin_bounced_mail_address_path(mail_addr), method: :delete, data: { confirm: 'Are you sure?' } %>
- -
- -<%= link_to 'New Bounced Mail Address', new_admin_bounced_mail_address_path %> + + <% @bounced_mail_addresses.each do |mail_addr| %> + + <%= mail_addr.email %> + <%= mail_addr.action %> + <%= mail_addr.status %> + <%= mail_addr.diagnostic %> + <%= mail_addr.created_at %> + <%= link_to 'Detailed', admin_bounced_mail_address_path(mail_addr) %> + <%= link_to 'Destroy', admin_bounced_mail_address_path(mail_addr), method: :delete, data: { confirm: 'Are you sure?' } %> + + <% end %> + + + + + + From 834b2c95bc7e56c2c4ba15a60d64f7cd9b89cbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 14:21:25 +0300 Subject: [PATCH 006/202] Add full request JSON to bounced mail, remove unused views --- .../bounced_mail_addresses_controller.rb | 35 +----------------- app/models/bounced_mail_address.rb | 15 ++++++-- app/views/admin/base/_menu.haml | 1 + .../bounced_mail_addresses/_form.html.erb | 37 ------------------- .../bounced_mail_addresses/edit.html.erb | 6 --- .../admin/bounced_mail_addresses/new.html.erb | 5 --- .../bounced_mail_addresses/show.html.erb | 10 +++-- config/locales/admin/menu.en.yml | 1 + ..._recipient_json_to_bounced_mail_address.rb | 5 +++ db/structure.sql | 6 ++- 10 files changed, 31 insertions(+), 90 deletions(-) delete mode 100644 app/views/admin/bounced_mail_addresses/_form.html.erb delete mode 100644 app/views/admin/bounced_mail_addresses/edit.html.erb delete mode 100644 app/views/admin/bounced_mail_addresses/new.html.erb create mode 100644 db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb diff --git a/app/controllers/admin/bounced_mail_addresses_controller.rb b/app/controllers/admin/bounced_mail_addresses_controller.rb index 551413e2c..da9421450 100644 --- a/app/controllers/admin/bounced_mail_addresses_controller.rb +++ b/app/controllers/admin/bounced_mail_addresses_controller.rb @@ -1,47 +1,16 @@ module Admin class BouncedMailAddressesController < BaseController - before_action :set_bounced_mail_address, only: %i[show edit update destroy] + before_action :set_bounced_mail_address, only: %i[show destroy] load_and_authorize_resource # GET /bounced_mail_addresses def index - @bounced_mail_addresses = BouncedMailAddress.all + @bounced_mail_addresses = BouncedMailAddress.all.order(created_at: :desc) end # GET /bounced_mail_addresses/1 def show; end - # GET /bounced_mail_addresses/new - def new - @bounced_mail_address = BouncedMailAddress.new - end - - # GET /bounced_mail_addresses/1/edit - def edit; end - - # POST /bounced_mail_addresses - def create - @bounced_mail_address = BouncedMailAddress.new(bounced_mail_address_params) - - if @bounced_mail_address.save - redirect_to( - admin_bounced_mail_addresses_url, - notice: 'Bounced mail address was successfully created.' - ) - else - render(:new) - end - end - - # PATCH/PUT /bounced_mail_addresses/1 - def update - if @bounced_mail_address.update(bounced_mail_address_params) - redirect_to(@bounced_mail_address, notice: 'Bounced mail address was successfully updated.') - else - render(:edit) - end - end - # DELETE /bounced_mail_addresses/1 def destroy @bounced_mail_address.destroy diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index 6609829da..78f37c374 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -1,23 +1,30 @@ class BouncedMailAddress < ApplicationRecord validates :email, presence: true validates :bounce_reason, presence: true + before_validation :assign_bounce_reason + + def assign_bounce_reason + self.bounce_reason = "#{action} (#{status} #{diagnostic})" + end def diagnostic - response_json['diagnosticCode'] + recipient_json['diagnosticCode'] end def action - response_json['action'] + recipient_json['action'] end def status - response_json['status'] + recipient_json['status'] end def self.record(json) bounced_records = json['bounce']['bouncedRecipients'] bounced_records.each do |record| - bounce_record = BouncedMailAddress.new(email: record['emailAddress'], response_json: record) + bounce_record = BouncedMailAddress.new(email: record['emailAddress'], recipient_json: record, + response_json: json) + bounce_record.save end end diff --git a/app/views/admin/base/_menu.haml b/app/views/admin/base/_menu.haml index a327419fd..5853bd3e6 100644 --- a/app/views/admin/base/_menu.haml +++ b/app/views/admin/base/_menu.haml @@ -33,6 +33,7 @@ %li= link_to t('.blocked_domains'), admin_blocked_domains_path %li= link_to t('.reserved_domains'), admin_reserved_domains_path %li= link_to t('.disputed_domains'), admin_disputes_path + %li= link_to t('.bounced_email_addresses'), admin_bounced_mail_addresses_path %li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today') %li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today') %li= link_to t('.que'), '/admin/que' diff --git a/app/views/admin/bounced_mail_addresses/_form.html.erb b/app/views/admin/bounced_mail_addresses/_form.html.erb deleted file mode 100644 index 7a384bd3f..000000000 --- a/app/views/admin/bounced_mail_addresses/_form.html.erb +++ /dev/null @@ -1,37 +0,0 @@ -<%= form_for([:admin, @bounced_mail_address], html: { class: 'form-horizontal' }) do |form| %> - <% if @bounced_mail_address.errors.any? %> -
-

<%= pluralize(bounced_mail_address.errors.count, "error") %> prohibited this bounced_mail_address from being saved:

- -
    - <% @bounced_mail_address.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
- <% end %> - -
- <%= form.label :email %> - <%= form.text_field :email %> -
- -
- <%= form.label :bounce_reason %> - <%= form.text_field :bounce_reason %> -
- -
- <%= form.label :incidents %> - <%= form.number_field :incidents %> -
- -
- <%= form.label :response_json %> - <%= form.text_field :response_json %> -
- -
- <%= form.submit %> -
-<% end %> diff --git a/app/views/admin/bounced_mail_addresses/edit.html.erb b/app/views/admin/bounced_mail_addresses/edit.html.erb deleted file mode 100644 index a3dfe2d84..000000000 --- a/app/views/admin/bounced_mail_addresses/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Editing Bounced Mail Address

- -<%= render 'form', bounced_mail_address: @bounced_mail_address %> - -<%= link_to 'Show', admin_bounced_mail_address_path(@bounced_mail_address)%> | -<%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/app/views/admin/bounced_mail_addresses/new.html.erb b/app/views/admin/bounced_mail_addresses/new.html.erb deleted file mode 100644 index 010ac79dc..000000000 --- a/app/views/admin/bounced_mail_addresses/new.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

New Bounced Mail Address

- -<%= render 'form', bounced_mail_address: @bounced_mail_address %> - -<%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/app/views/admin/bounced_mail_addresses/show.html.erb b/app/views/admin/bounced_mail_addresses/show.html.erb index 1f48ad5cf..752bd7b0a 100644 --- a/app/views/admin/bounced_mail_addresses/show.html.erb +++ b/app/views/admin/bounced_mail_addresses/show.html.erb @@ -16,9 +16,13 @@

- Response json: - <%= @bounced_mail_address.response_json %> + Bounced recipient JSON: +

<%= JSON.pretty_generate(@bounced_mail_address.recipient_json) %>
+

+ +

+ Bounce payload: +

<%= JSON.pretty_generate(@bounced_mail_address.response_json) %>

-<%= link_to 'Edit', edit_admin_bounced_mail_address_path(@bounced_mail_address) %> | <%= link_to 'Back', admin_bounced_mail_addresses_path %> diff --git a/config/locales/admin/menu.en.yml b/config/locales/admin/menu.en.yml index 617341c6a..cb1060e6f 100644 --- a/config/locales/admin/menu.en.yml +++ b/config/locales/admin/menu.en.yml @@ -14,6 +14,7 @@ en: blocked_domains: Blocked domains reserved_domains: Reserved domains disputed_domains: Disputed domains + bounced_email_addresses: Bounced emails epp_log: EPP log repp_log: REPP log que: Que diff --git a/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb b/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb new file mode 100644 index 000000000..bad3d846e --- /dev/null +++ b/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb @@ -0,0 +1,5 @@ +class AddRecipientJsonToBouncedMailAddress < ActiveRecord::Migration[6.0] + def change + add_column :bounced_mail_addresses, :recipient_json, :jsonb, null: false + end +end diff --git a/db/structure.sql b/db/structure.sql index 3ec055386..23669c665 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -486,7 +486,8 @@ CREATE TABLE public.bounced_mail_addresses ( incidents integer DEFAULT 1 NOT NULL, response_json jsonb, created_at timestamp(6) without time zone NOT NULL, - updated_at timestamp(6) without time zone NOT NULL + updated_at timestamp(6) without time zone NOT NULL, + recipient_json jsonb NOT NULL ); @@ -4956,6 +4957,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200908131554'), ('20200910085157'), ('20200910102028'), -('20200916125326'); +('20200916125326'), +('20200917104213'); From b2c5a9a5ec69ac8b27e7fa7ab8fef96f292f1485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 15:55:37 +0300 Subject: [PATCH 007/202] Verify param integrity for bounces --- app/controllers/api/v1/bounces_controller.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/bounces_controller.rb b/app/controllers/api/v1/bounces_controller.rb index cff8c3efe..40a3c1c91 100644 --- a/app/controllers/api/v1/bounces_controller.rb +++ b/app/controllers/api/v1/bounces_controller.rb @@ -1,11 +1,20 @@ module Api module V1 class BouncesController < BaseController - before_action :authenticate - + # POST api/v1/bounces/ def create - bounced_mail_address = BouncedMailAddress.record(json) - bounced_mail_address ? render(head: :ok) : render(head: :failed) + BouncedMailAddress.record(bounce_params) + head(:ok) + rescue ActionController::ParameterMissing + head(:bad_request) + end + + def bounce_params + params.require(:data).require(:bounce).require(:bouncedRecipients).each do |r| + r.require(:emailAddress) + end + + params.require(:data) end end end From 03182f92227acceae7c1cf6a08e192e1c44472d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 17 Sep 2020 16:26:50 +0300 Subject: [PATCH 008/202] Implement shared key authentication to bounces API --- app/controllers/api/v1/base_controller.rb | 5 +++++ app/controllers/api/v1/bounces_controller.rb | 4 +++- config/application.yml.sample | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 54930edf9..b62b3e063 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -10,6 +10,11 @@ module Api head :unauthorized unless ip_allowed end + def authenticate_shared_key + api_key = "Basic #{ENV['api_shared_key']}" + head(:unauthorized) unless api_key == request.authorization + end + def not_found_error uuid = params['uuid'] json = { error: 'Not Found', uuid: uuid, message: 'Record not found' } diff --git a/app/controllers/api/v1/bounces_controller.rb b/app/controllers/api/v1/bounces_controller.rb index 40a3c1c91..296c9d9bd 100644 --- a/app/controllers/api/v1/bounces_controller.rb +++ b/app/controllers/api/v1/bounces_controller.rb @@ -1,10 +1,12 @@ module Api module V1 class BouncesController < BaseController + before_action :authenticate_shared_key + # POST api/v1/bounces/ def create BouncedMailAddress.record(bounce_params) - head(:ok) + head(:created) rescue ActionController::ParameterMissing head(:bad_request) end diff --git a/config/application.yml.sample b/config/application.yml.sample index 72b55e2ea..237617be3 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -87,6 +87,9 @@ sk_digi_doc_service_name: 'Testimine' registrant_api_base_url: registrant_api_auth_allowed_ips: '127.0.0.1, 0.0.0.0' #ips, separated with commas +# Bounces API +api_shared_key: testkey + # # MISC From 036a3a37207f4a89d01aad6361e59bd7d7297ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Sep 2020 10:52:14 +0300 Subject: [PATCH 009/202] Return empty user object when authorized user not found --- app/controllers/registrar/sessions_controller.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/controllers/registrar/sessions_controller.rb b/app/controllers/registrar/sessions_controller.rb index 5bebe5619..709e66955 100644 --- a/app/controllers/registrar/sessions_controller.rb +++ b/app/controllers/registrar/sessions_controller.rb @@ -158,12 +158,15 @@ class Registrar def find_user_by_idc_and_allowed(idc) return User.new unless idc + possible_users = ApiUser.where(identity_code: idc) || User.new possible_users.each do |selected_user| - if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip) - return selected_user - end + next unless selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip) + + return selected_user end + + User.new end def check_ip_restriction From 7d8c24533af3727b0817db6822b89eb35b373472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Sep 2020 14:44:40 +0300 Subject: [PATCH 010/202] Create unit tests for bounced mail address --- app/models/bounced_mail_address.rb | 11 ++++- test/models/bounced_mail_address_test.rb | 61 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 test/models/bounced_mail_address_test.rb diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index 78f37c374..f12c7f19c 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -1,21 +1,28 @@ class BouncedMailAddress < ApplicationRecord validates :email, presence: true - validates :bounce_reason, presence: true + validates :bounce_reason, :recipient_json, :response_json, presence: true before_validation :assign_bounce_reason def assign_bounce_reason - self.bounce_reason = "#{action} (#{status} #{diagnostic})" + if recipient_json + self.bounce_reason = "#{action} (#{status} #{diagnostic})" + else + self.bounce_reason = nil + end end def diagnostic + return nil unless recipient_json recipient_json['diagnosticCode'] end def action + return nil unless recipient_json recipient_json['action'] end def status + return nil unless recipient_json recipient_json['status'] end diff --git a/test/models/bounced_mail_address_test.rb b/test/models/bounced_mail_address_test.rb new file mode 100644 index 000000000..2caea7d98 --- /dev/null +++ b/test/models/bounced_mail_address_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +class BouncedMailAddressTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + def setup + @bounced_mail = BouncedMailAddress.new + @bounced_mail.email = 'recipient@registry.test' + @bounced_mail.bounce_reason = 'failed (5.1.1 smtp; 550 5.1.1 user unknown)' + @bounced_mail.response_json = {"mail"=>{"source"=>"noreply@internet.test", "sourceIp"=>"195.43.86.5", "messageId"=>"010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", "sourceArn"=>"arn:aws:ses:us-east-2:650268220328:identity/noreply@internet.test", "timestamp"=>"2020-09-18T10:34:44.000Z", "destination"=>["#{@bounced_mail.email}"], "sendingAccountId"=>"650268220328"}, "bounce"=>{"timestamp"=>"2020-09-18T10:34:44.911Z", "bounceType"=>"Permanent", "feedbackId"=>"010f0174a0c7d4f9-27d59756-6111-xxxx-a507-26bee0d55fa2-000000", "remoteMtaIp"=>"127.0.0.1", "reportingMTA"=>"dsn; xxx.amazonses.com", "bounceSubType"=>"General", "bouncedRecipients"=>[{"action"=>"failed", "status"=>"5.1.1", "emailAddress"=>"#{@bounced_mail.email}", "diagnosticCode"=>"smtp; 550 5.1.1 user unknown"}]}, "notificationType"=>"Bounce"} + @bounced_mail.recipient_json = {"action"=>"failed", "status"=>"5.1.1", "emailAddress"=>"#{@bounced_mail.email}", "diagnosticCode"=>"smtp; 550 5.1.1 user unknown"} + + end + + def test_bounce_reason_is_autoassigned + assert @bounced_mail.valid? + @bounced_mail.bounce_reason = nil + assert @bounced_mail.valid? + + assert_equal 'failed (5.1.1 smtp; 550 5.1.1 user unknown)', @bounced_mail.bounce_reason + end + + def test_response_json_is_required + assert @bounced_mail.valid? + @bounced_mail.response_json = nil + assert_not @bounced_mail.valid? + assert @bounced_mail.errors.full_messages.include? 'Response json is missing' + end + + def test_recipient_json_is_required + assert @bounced_mail.valid? + @bounced_mail.recipient_json = nil + assert_not @bounced_mail.valid? + + assert @bounced_mail.errors.full_messages.include? 'Recipient json is missing' + end + + def test_status_is_determined_dynamically + assert @bounced_mail.valid? + assert_equal '5.1.1', @bounced_mail.status + @bounced_mail.recipient_json['status'] = 'xxx_status' + assert_equal 'xxx_status', @bounced_mail.status + end + + def test_action_is_determined_dynamically + assert @bounced_mail.valid? + assert_equal 'failed', @bounced_mail.action + @bounced_mail.recipient_json['action'] = 'xxx_action' + assert_equal 'xxx_action', @bounced_mail.action + end + + def test_diagnostic_is_determined_dynamically + assert @bounced_mail.valid? + assert_equal 'smtp; 550 5.1.1 user unknown', @bounced_mail.diagnostic + @bounced_mail.recipient_json['diagnosticCode'] = 'xxx_diagnostic' + assert_equal 'xxx_diagnostic', @bounced_mail.diagnostic + end + + def test_creates_objects_from_sns_json + end +end From 64308e11040fcc172ce2dd30b25a7179258e58a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Sep 2020 14:49:27 +0300 Subject: [PATCH 011/202] Fix CC issues --- .../registrar/sessions_controller.rb | 8 ++-- app/models/bounced_mail_address.rb | 17 +++++---- test/models/bounced_mail_address_test.rb | 38 +++++++++++++++++++ 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/app/controllers/registrar/sessions_controller.rb b/app/controllers/registrar/sessions_controller.rb index 709e66955..df90ea57b 100644 --- a/app/controllers/registrar/sessions_controller.rb +++ b/app/controllers/registrar/sessions_controller.rb @@ -161,12 +161,10 @@ class Registrar possible_users = ApiUser.where(identity_code: idc) || User.new possible_users.each do |selected_user| - next unless selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip) - - return selected_user + if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip) + return selected_user + end end - - User.new end def check_ip_restriction diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index f12c7f19c..02bb42337 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -4,25 +4,26 @@ class BouncedMailAddress < ApplicationRecord before_validation :assign_bounce_reason def assign_bounce_reason - if recipient_json - self.bounce_reason = "#{action} (#{status} #{diagnostic})" - else - self.bounce_reason = nil - end + return self.bounce_reason = nil unless recipient_json + + self.bounce_reason = "#{action} (#{status} #{diagnostic})" end def diagnostic - return nil unless recipient_json + return unless recipient_json + recipient_json['diagnosticCode'] end def action - return nil unless recipient_json + return unless recipient_json + recipient_json['action'] end def status - return nil unless recipient_json + return unless recipient_json + recipient_json['status'] end diff --git a/test/models/bounced_mail_address_test.rb b/test/models/bounced_mail_address_test.rb index 2caea7d98..0e6a62ab8 100644 --- a/test/models/bounced_mail_address_test.rb +++ b/test/models/bounced_mail_address_test.rb @@ -57,5 +57,43 @@ class BouncedMailAddressTest < ActiveSupport::TestCase end def test_creates_objects_from_sns_json + BouncedMailAddress.record(sns_bounce_payload) + + bounced_mail = BouncedMailAddress.last + assert_equal domains(:shop).registrant.email, bounced_mail.email + assert_equal 'failed', bounced_mail.action + assert_equal '5.1.1', bounced_mail.status + assert_equal 'smtp; 550 5.1.1 user unknown', bounced_mail.diagnostic + end + + def sns_bounce_payload + { + "notificationType": "Bounce", + "mail": { + "source": "noreply@registry.test", + "sourceIp": "195.43.86.5", + "messageId": "010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", + "sourceArn": "arn:aws:ses:us-east-2:65026820000:identity/noreply@registry.test", + "timestamp": "2020-09-18T10:34:44.000Z", + "destination": [ "#{domains(:shop).registrant.email}" ], + "sendingAccountId": "650268220000" + }, + "bounce": { + "timestamp": "2020-09-18T10:34:44.911Z", + "bounceType": "Permanent", + "feedbackId": "010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000", + "remoteMtaIp": "127.0.01", + "reportingMTA": "dsn; xxx.amazonses.com", + "bounceSubType": "General", + "bouncedRecipients": [ + { + "action": "failed", + "status": "5.1.1", + "emailAddress": "#{domains(:shop).registrant.email}", + "diagnosticCode": "smtp; 550 5.1.1 user unknown" + } + ] + } + }.as_json end end From 101d5d4a7846fea4c3e242dcf3c57af5eb4b5bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Sep 2020 16:17:42 +0300 Subject: [PATCH 012/202] Add tests for admin bounced mails views --- .../bounced_mail_addresses_controller.rb | 10 ----- .../bounced_mail_addresses/index.html.erb | 2 - .../bounced_mail_addresses/show.html.erb | 4 +- test/fixtures/bounced_mail_addresses.yml | 40 ++++++++++++++++++ .../admin_area/bounced_mail_addresses_test.rb | 41 +++++++++++++++++++ 5 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/bounced_mail_addresses.yml create mode 100644 test/system/admin_area/bounced_mail_addresses_test.rb diff --git a/app/controllers/admin/bounced_mail_addresses_controller.rb b/app/controllers/admin/bounced_mail_addresses_controller.rb index da9421450..1c59acaa4 100644 --- a/app/controllers/admin/bounced_mail_addresses_controller.rb +++ b/app/controllers/admin/bounced_mail_addresses_controller.rb @@ -26,15 +26,5 @@ module Admin def set_bounced_mail_address @bounced_mail_address = BouncedMailAddress.find(params[:id]) end - - # Only allow a trusted parameter "white list" through. - def bounced_mail_address_params - params.require(:bounced_mail_address).permit( - :email, - :bounce_reason, - :incidents, - :response_json - ) - end end end diff --git a/app/views/admin/bounced_mail_addresses/index.html.erb b/app/views/admin/bounced_mail_addresses/index.html.erb index b6a019282..913cbd19d 100644 --- a/app/views/admin/bounced_mail_addresses/index.html.erb +++ b/app/views/admin/bounced_mail_addresses/index.html.erb @@ -1,5 +1,3 @@ -

<%= notice %>

-

Bounced Mail Addresses

diff --git a/app/views/admin/bounced_mail_addresses/show.html.erb b/app/views/admin/bounced_mail_addresses/show.html.erb index 752bd7b0a..9b0b5919f 100644 --- a/app/views/admin/bounced_mail_addresses/show.html.erb +++ b/app/views/admin/bounced_mail_addresses/show.html.erb @@ -1,6 +1,3 @@ -

<%= notice %>

- -

Email: <%= @bounced_mail_address.email %>

@@ -26,3 +23,4 @@

<%= link_to 'Back', admin_bounced_mail_addresses_path %> +<%= link_to 'Destroy', admin_bounced_mail_address_path(@bounced_mail_address), method: :delete, data: { confirm: 'Are you sure?' } %> diff --git a/test/fixtures/bounced_mail_addresses.yml b/test/fixtures/bounced_mail_addresses.yml new file mode 100644 index 000000000..1261f1429 --- /dev/null +++ b/test/fixtures/bounced_mail_addresses.yml @@ -0,0 +1,40 @@ +one: + email: bounced@registry.test + bounce_reason: failed (5.1.1 smtp; 550 5.1.1 user unknown) + incidents: 1 + recipient_json: { + "action": "failed", + "status": "5.1.1", + "emailAddress": "bounced@registry.test", + "diagnosticCode": "smtp; 550 5.1.1 user unknown" + } + response_json: { + "notificationType": "Bounce", + "mail": { + "source": "noreply@registry.test", + "sourceIp": "195.43.86.5", + "messageId": "010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", + "sourceArn": "arn:aws:ses:us-east-2:65026820000:identity/noreply@registry.test", + "timestamp": "2020-09-18T10:34:44.000Z", + "destination": [ "bounced@registry.test" ], + "sendingAccountId": "650268220000" + }, + "bounce": { + "timestamp": "2020-09-18T10:34:44.911Z", + "bounceType": "Permanent", + "feedbackId": "010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000", + "remoteMtaIp": "127.0.01", + "reportingMTA": "dsn; xxx.amazonses.com", + "bounceSubType": "General", + "bouncedRecipients": [ + { + "action": "failed", + "status": "5.1.1", + "emailAddress": "bounced@registry.test", + "diagnosticCode": "smtp; 550 5.1.1 user unknown" + } + ] + } + } + created_at: <%= Time.zone.parse('2010-07-05').to_s(:db) %> + updated_at: <%= Time.zone.parse('2010-07-05').to_s(:db) %> diff --git a/test/system/admin_area/bounced_mail_addresses_test.rb b/test/system/admin_area/bounced_mail_addresses_test.rb new file mode 100644 index 000000000..5500f4375 --- /dev/null +++ b/test/system/admin_area/bounced_mail_addresses_test.rb @@ -0,0 +1,41 @@ +require 'application_system_test_case' + +class AdminBouncedMailAddressesTest < ApplicationSystemTestCase + include ActionView::Helpers::NumberHelper + + def setup + @bounced_mail = bounced_mail_addresses(:one) + @original_default_language = Setting.default_language + sign_in users(:admin) + end + + def teardown + Setting.default_language = @original_default_language + end + + def test_shows_bounced_emails + visit admin_bounced_mail_addresses_path + assert_text @bounced_mail.status + assert_text @bounced_mail.action + assert_text @bounced_mail.diagnostic + assert_text @bounced_mail.email + end + + def test_shows_detailed_bounced_email + visit admin_bounced_mail_address_path(@bounced_mail) + assert_text @bounced_mail.status + assert_text @bounced_mail.action + assert_text @bounced_mail.diagnostic + assert_text @bounced_mail.email + + assert_text 'bouncedRecipients' + assert_text '010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000' + end + + def test_deletes_registrar + visit admin_bounced_mail_address_path(@bounced_mail) + click_on 'Destroy' + + assert_text 'Bounced mail address was successfully destroyed.' + end +end From 6a93395fa493252f5f717799a0f5943257e1ddb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Sep 2020 11:37:31 +0300 Subject: [PATCH 013/202] Add integration tests for Bounces API --- app/controllers/api/v1/bounces_controller.rb | 2 + .../integration/api/v1/bounces/create_test.rb | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 test/integration/api/v1/bounces/create_test.rb diff --git a/app/controllers/api/v1/bounces_controller.rb b/app/controllers/api/v1/bounces_controller.rb index 296c9d9bd..fd10a3398 100644 --- a/app/controllers/api/v1/bounces_controller.rb +++ b/app/controllers/api/v1/bounces_controller.rb @@ -5,6 +5,8 @@ module Api # POST api/v1/bounces/ def create + return head(:bad_request) unless bounce_params[:bounce][:bouncedRecipients].any? + BouncedMailAddress.record(bounce_params) head(:created) rescue ActionController::ParameterMissing diff --git a/test/integration/api/v1/bounces/create_test.rb b/test/integration/api/v1/bounces/create_test.rb new file mode 100644 index 000000000..899b6c5c7 --- /dev/null +++ b/test/integration/api/v1/bounces/create_test.rb @@ -0,0 +1,75 @@ +require 'test_helper' + +class BouncesApiV1CreateTest < ActionDispatch::IntegrationTest + def setup + @api_key = "Basic #{ENV['api_shared_key']}" + @headers = { "Authorization": "#{@api_key}" } + @json_body = { "data": valid_bounce_request }.as_json + end + + def test_authorizes_api_request + post api_v1_bounces_path, params: @json_body, headers: @headers + assert_response :created + + invalid_headers = { "Authorization": "Basic invalid_api_key" } + post api_v1_bounces_path, params: @json_body, headers: invalid_headers + assert_response :unauthorized + end + + def test_returns_bad_request_if_invalid_payload + invalid_json_body = @json_body.dup + invalid_json_body['data']['bounce']['bouncedRecipients'] = nil + + post api_v1_bounces_path, params: invalid_json_body, headers: @headers + assert_response :bad_request + + invalid_json_body = 'aaaa' + post api_v1_bounces_path, params: invalid_json_body, headers: @headers + assert_response :bad_request + end + + def test_saves_new_bounce_object + request_body = @json_body.dup + random_mail = "#{rand(10000..99999)}@registry.test" + request_body['data']['bounce']['bouncedRecipients'][0]['emailAddress'] = random_mail + + post api_v1_bounces_path, params: request_body, headers: @headers + assert_response :created + + bounced_mail = BouncedMailAddress.last + assert bounced_mail.email = random_mail + assert '5.1.1', bounced_mail.status + assert 'failed', bounced_mail.action + end + + def valid_bounce_request + { + "notificationType": "Bounce", + "mail": { + "source": "noreply@registry.test", + "sourceIp": "195.43.86.5", + "messageId": "010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", + "sourceArn": "arn:aws:ses:us-east-2:65026820000:identity/noreply@registry.test", + "timestamp": "2020-09-18T10:34:44.000Z", + "destination": [ "bounced@registry.test" ], + "sendingAccountId": "650268220000" + }, + "bounce": { + "timestamp": "2020-09-18T10:34:44.911Z", + "bounceType": "Permanent", + "feedbackId": "010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000", + "remoteMtaIp": "127.0.01", + "reportingMTA": "dsn; xxx.amazonses.com", + "bounceSubType": "General", + "bouncedRecipients": [ + { + "action": "failed", + "status": "5.1.1", + "emailAddress": "bounced@registry.test", + "diagnosticCode": "smtp; 550 5.1.1 user unknown" + } + ] + } + }.as_json + end +end From 818869d249d4e5ebdf1945f454968a0ebcd8c78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Sep 2020 11:46:42 +0300 Subject: [PATCH 014/202] Remove incidents count from bounced mails --- app/views/admin/bounced_mail_addresses/show.html.erb | 5 ----- ...84356_remove_incidents_from_bounced_mail_addresses.rb | 9 +++++++++ db/structure.sql | 4 ++-- test/fixtures/bounced_mail_addresses.yml | 1 - 4 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb diff --git a/app/views/admin/bounced_mail_addresses/show.html.erb b/app/views/admin/bounced_mail_addresses/show.html.erb index 9b0b5919f..98eeabcd2 100644 --- a/app/views/admin/bounced_mail_addresses/show.html.erb +++ b/app/views/admin/bounced_mail_addresses/show.html.erb @@ -7,11 +7,6 @@ <%= @bounced_mail_address.bounce_reason %>

-

- Incidents: - <%= @bounced_mail_address.incidents %> -

-

Bounced recipient JSON:

<%= JSON.pretty_generate(@bounced_mail_address.recipient_json) %>
diff --git a/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb b/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb new file mode 100644 index 000000000..0704795df --- /dev/null +++ b/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb @@ -0,0 +1,9 @@ +class RemoveIncidentsFromBouncedMailAddresses < ActiveRecord::Migration[6.0] + def up + remove_column :bounced_mail_addresses, :incidents + end + + def down + add_column :bounced_mail_addresses, :incidents, :integer, null: false, default: 1 + end +end diff --git a/db/structure.sql b/db/structure.sql index 23669c665..74e784408 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -483,7 +483,6 @@ CREATE TABLE public.bounced_mail_addresses ( id bigint NOT NULL, email character varying NOT NULL, bounce_reason character varying NOT NULL, - incidents integer DEFAULT 1 NOT NULL, response_json jsonb, created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, @@ -4958,6 +4957,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200910085157'), ('20200910102028'), ('20200916125326'), -('20200917104213'); +('20200917104213'), +('20200921084356'); diff --git a/test/fixtures/bounced_mail_addresses.yml b/test/fixtures/bounced_mail_addresses.yml index 1261f1429..345b2ef22 100644 --- a/test/fixtures/bounced_mail_addresses.yml +++ b/test/fixtures/bounced_mail_addresses.yml @@ -1,7 +1,6 @@ one: email: bounced@registry.test bounce_reason: failed (5.1.1 smtp; 550 5.1.1 user unknown) - incidents: 1 recipient_json: { "action": "failed", "status": "5.1.1", From fae620c19d87fc4df9710be88e3608a4192b29a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Sep 2020 13:12:23 +0300 Subject: [PATCH 015/202] Define routing scope for :bounced_mail_addresses --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 89f8f0cd6..9938403e7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -299,7 +299,7 @@ Rails.application.routes.draw do resources :delayed_jobs resources :epp_logs resources :repp_logs - resources :bounced_mail_addresses + resources :bounced_mail_addresses, only: %i[index show destroy] authenticate :admin_user do mount Que::Web, at: 'que' From 659cb7f4e67ee51545b6ea0ce5b20e030e881591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Sep 2020 13:34:34 +0300 Subject: [PATCH 016/202] Combine migrations, remove json objects from bounced mails --- ...0200916125326_create_bounced_mail_addresses.rb | 9 ++++++--- ..._add_recipient_json_to_bounced_mail_address.rb | 5 ----- ...emove_incidents_from_bounced_mail_addresses.rb | 9 --------- db/structure.sql | 15 ++++++++------- 4 files changed, 14 insertions(+), 24 deletions(-) delete mode 100644 db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb delete mode 100644 db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb diff --git a/db/migrate/20200916125326_create_bounced_mail_addresses.rb b/db/migrate/20200916125326_create_bounced_mail_addresses.rb index c6600afea..e1744cc9a 100644 --- a/db/migrate/20200916125326_create_bounced_mail_addresses.rb +++ b/db/migrate/20200916125326_create_bounced_mail_addresses.rb @@ -2,9 +2,12 @@ class CreateBouncedMailAddresses < ActiveRecord::Migration[6.0] def change create_table :bounced_mail_addresses do |t| t.string :email, null: false - t.string :bounce_reason, null: false - t.integer :incidents, null: false, default: 1 - t.jsonb :response_json + t.string :message_id, null: false + t.string :bounce_type, null: false + t.string :bounce_subtype, null: false + t.string :action, null: false + t.string :status, null: false + t.string :diagnostic, null: true t.timestamps end diff --git a/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb b/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb deleted file mode 100644 index bad3d846e..000000000 --- a/db/migrate/20200917104213_add_recipient_json_to_bounced_mail_address.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddRecipientJsonToBouncedMailAddress < ActiveRecord::Migration[6.0] - def change - add_column :bounced_mail_addresses, :recipient_json, :jsonb, null: false - end -end diff --git a/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb b/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb deleted file mode 100644 index 0704795df..000000000 --- a/db/migrate/20200921084356_remove_incidents_from_bounced_mail_addresses.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RemoveIncidentsFromBouncedMailAddresses < ActiveRecord::Migration[6.0] - def up - remove_column :bounced_mail_addresses, :incidents - end - - def down - add_column :bounced_mail_addresses, :incidents, :integer, null: false, default: 1 - end -end diff --git a/db/structure.sql b/db/structure.sql index 74e784408..e064d968f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -482,11 +482,14 @@ ALTER SEQUENCE public.blocked_domains_id_seq OWNED BY public.blocked_domains.id; CREATE TABLE public.bounced_mail_addresses ( id bigint NOT NULL, email character varying NOT NULL, - bounce_reason character varying NOT NULL, - response_json jsonb, + message_id character varying NOT NULL, + bounce_type character varying NOT NULL, + bounce_subtype character varying NOT NULL, + action character varying NOT NULL, + status character varying NOT NULL, + diagnostic character varying, created_at timestamp(6) without time zone NOT NULL, - updated_at timestamp(6) without time zone NOT NULL, - recipient_json jsonb NOT NULL + updated_at timestamp(6) without time zone NOT NULL ); @@ -4956,8 +4959,6 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200908131554'), ('20200910085157'), ('20200910102028'), -('20200916125326'), -('20200917104213'), -('20200921084356'); +('20200916125326'); From 98674ab3817148e6c5e15acd7b48a956e3ef58d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Sep 2020 13:47:57 +0300 Subject: [PATCH 017/202] Reflect new bounced mail structure --- app/models/bounced_mail_address.rb | 36 ++++++++----------- .../bounced_mail_addresses/show.html.erb | 18 ++++++---- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index 02bb42337..61679f543 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -1,6 +1,5 @@ class BouncedMailAddress < ApplicationRecord - validates :email, presence: true - validates :bounce_reason, :recipient_json, :response_json, presence: true + validates :email, :message_id, :bounce_type, :bounce_subtype, :action, :status, presence: true before_validation :assign_bounce_reason def assign_bounce_reason @@ -9,31 +8,24 @@ class BouncedMailAddress < ApplicationRecord self.bounce_reason = "#{action} (#{status} #{diagnostic})" end - def diagnostic - return unless recipient_json - - recipient_json['diagnosticCode'] - end - - def action - return unless recipient_json - - recipient_json['action'] - end - - def status - return unless recipient_json - - recipient_json['status'] - end - def self.record(json) bounced_records = json['bounce']['bouncedRecipients'] bounced_records.each do |record| - bounce_record = BouncedMailAddress.new(email: record['emailAddress'], recipient_json: record, - response_json: json) + bounce_record = BouncedMailAddress.new(params_from_json(json, record)) bounce_record.save end end + + def params_from_json(json, bounced_record) + { + email: bounced_record['emailAddress'], + message_id: json['mail']['messageId'], + bounce_type: json['bounce']['bounceType'], + bounce_subtype: json['bounce']['bounceSubType'], + action: bounced_record['action'], + status: bounced_record['status'], + diagnostic: bounced_record['diagnosticCode'], + } + end end diff --git a/app/views/admin/bounced_mail_addresses/show.html.erb b/app/views/admin/bounced_mail_addresses/show.html.erb index 98eeabcd2..5183ae5a1 100644 --- a/app/views/admin/bounced_mail_addresses/show.html.erb +++ b/app/views/admin/bounced_mail_addresses/show.html.erb @@ -1,20 +1,26 @@ +

Email: <%= @bounced_mail_address.email %>

- Bounce reason: - <%= @bounced_mail_address.bounce_reason %> + Bounced message ID: + <%= @bounced_mail_address.message_id %>

- Bounced recipient JSON: -

<%= JSON.pretty_generate(@bounced_mail_address.recipient_json) %>
+ Overall bounce type: + <%= @bounced_mail_address.bounce_type %> (<%= @bounced_mail_address.bounce_subtype %> )

- Bounce payload: -

<%= JSON.pretty_generate(@bounced_mail_address.response_json) %>
+ Bounced recipient status: + <%= @bounced_mail_address.action %> (<%= @bounced_mail_address.status %>) +

+ +

+ Bounced recipient diagnostic: +

<%= @bounced_mail_address.diagnostic %>

<%= link_to 'Back', admin_bounced_mail_addresses_path %> From 3222a8b9a79b80444a41b8b348c98d8d260aeb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 30 Sep 2020 12:48:21 +0300 Subject: [PATCH 018/202] Bounces: Fix tests --- app/models/bounced_mail_address.rb | 9 +- test/fixtures/bounced_mail_addresses.yml | 41 ++------- test/models/bounced_mail_address_test.rb | 91 ++++++++++--------- .../admin_area/bounced_mail_addresses_test.rb | 3 +- 4 files changed, 58 insertions(+), 86 deletions(-) diff --git a/app/models/bounced_mail_address.rb b/app/models/bounced_mail_address.rb index 61679f543..73c6a0941 100644 --- a/app/models/bounced_mail_address.rb +++ b/app/models/bounced_mail_address.rb @@ -1,11 +1,8 @@ class BouncedMailAddress < ApplicationRecord validates :email, :message_id, :bounce_type, :bounce_subtype, :action, :status, presence: true - before_validation :assign_bounce_reason - def assign_bounce_reason - return self.bounce_reason = nil unless recipient_json - - self.bounce_reason = "#{action} (#{status} #{diagnostic})" + def bounce_reason + "#{action} (#{status} #{diagnostic})" end def self.record(json) @@ -17,7 +14,7 @@ class BouncedMailAddress < ApplicationRecord end end - def params_from_json(json, bounced_record) + def self.params_from_json(json, bounced_record) { email: bounced_record['emailAddress'], message_id: json['mail']['messageId'], diff --git a/test/fixtures/bounced_mail_addresses.yml b/test/fixtures/bounced_mail_addresses.yml index 345b2ef22..cbfdff680 100644 --- a/test/fixtures/bounced_mail_addresses.yml +++ b/test/fixtures/bounced_mail_addresses.yml @@ -1,39 +1,10 @@ one: email: bounced@registry.test - bounce_reason: failed (5.1.1 smtp; 550 5.1.1 user unknown) - recipient_json: { - "action": "failed", - "status": "5.1.1", - "emailAddress": "bounced@registry.test", - "diagnosticCode": "smtp; 550 5.1.1 user unknown" - } - response_json: { - "notificationType": "Bounce", - "mail": { - "source": "noreply@registry.test", - "sourceIp": "195.43.86.5", - "messageId": "010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", - "sourceArn": "arn:aws:ses:us-east-2:65026820000:identity/noreply@registry.test", - "timestamp": "2020-09-18T10:34:44.000Z", - "destination": [ "bounced@registry.test" ], - "sendingAccountId": "650268220000" - }, - "bounce": { - "timestamp": "2020-09-18T10:34:44.911Z", - "bounceType": "Permanent", - "feedbackId": "010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000", - "remoteMtaIp": "127.0.01", - "reportingMTA": "dsn; xxx.amazonses.com", - "bounceSubType": "General", - "bouncedRecipients": [ - { - "action": "failed", - "status": "5.1.1", - "emailAddress": "bounced@registry.test", - "diagnosticCode": "smtp; 550 5.1.1 user unknown" - } - ] - } - } + message_id: 010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000 + bounce_type: Permanent + bounce_subtype: General + action: failed + status: '5.1.1' + diagnostic: 'smtp; 550 5.1.1 user unknown' created_at: <%= Time.zone.parse('2010-07-05').to_s(:db) %> updated_at: <%= Time.zone.parse('2010-07-05').to_s(:db) %> diff --git a/test/models/bounced_mail_address_test.rb b/test/models/bounced_mail_address_test.rb index 0e6a62ab8..4af401711 100644 --- a/test/models/bounced_mail_address_test.rb +++ b/test/models/bounced_mail_address_test.rb @@ -6,56 +6,61 @@ class BouncedMailAddressTest < ActiveSupport::TestCase def setup @bounced_mail = BouncedMailAddress.new @bounced_mail.email = 'recipient@registry.test' - @bounced_mail.bounce_reason = 'failed (5.1.1 smtp; 550 5.1.1 user unknown)' - @bounced_mail.response_json = {"mail"=>{"source"=>"noreply@internet.test", "sourceIp"=>"195.43.86.5", "messageId"=>"010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000", "sourceArn"=>"arn:aws:ses:us-east-2:650268220328:identity/noreply@internet.test", "timestamp"=>"2020-09-18T10:34:44.000Z", "destination"=>["#{@bounced_mail.email}"], "sendingAccountId"=>"650268220328"}, "bounce"=>{"timestamp"=>"2020-09-18T10:34:44.911Z", "bounceType"=>"Permanent", "feedbackId"=>"010f0174a0c7d4f9-27d59756-6111-xxxx-a507-26bee0d55fa2-000000", "remoteMtaIp"=>"127.0.0.1", "reportingMTA"=>"dsn; xxx.amazonses.com", "bounceSubType"=>"General", "bouncedRecipients"=>[{"action"=>"failed", "status"=>"5.1.1", "emailAddress"=>"#{@bounced_mail.email}", "diagnosticCode"=>"smtp; 550 5.1.1 user unknown"}]}, "notificationType"=>"Bounce"} - @bounced_mail.recipient_json = {"action"=>"failed", "status"=>"5.1.1", "emailAddress"=>"#{@bounced_mail.email}", "diagnosticCode"=>"smtp; 550 5.1.1 user unknown"} - + @bounced_mail.message_id = '010f0174a0c7d348-ea6e2fc1-0854-4073-b71f-5cecf9b0d0b2-000000' + @bounced_mail.bounce_type = 'Permanent' + @bounced_mail.bounce_subtype = 'General' + @bounced_mail.action = 'failed' + @bounced_mail.status = '5.1.1' + @bounced_mail.diagnostic = 'smtp; 550 5.1.1 user unknown' end - def test_bounce_reason_is_autoassigned - assert @bounced_mail.valid? - @bounced_mail.bounce_reason = nil + def test_email_is_required assert @bounced_mail.valid? + @bounced_mail.email = nil + assert @bounced_mail.invalid? + end + def test_message_id_is_required + assert @bounced_mail.valid? + @bounced_mail.message_id = nil + assert @bounced_mail.invalid? + end + + def test_bounce_type_is_required + assert @bounced_mail.valid? + @bounced_mail.bounce_type = nil + assert @bounced_mail.invalid? + end + + def test_bounce_subtype_is_required + assert @bounced_mail.valid? + @bounced_mail.bounce_subtype = nil + assert @bounced_mail.invalid? + end + + def test_action_is_required + assert @bounced_mail.valid? + @bounced_mail.action = nil + assert @bounced_mail.invalid? + end + + def test_status_is_required + assert @bounced_mail.valid? + @bounced_mail.status = nil + assert @bounced_mail.invalid? + end + + def test_diagnostic_is_not_required + assert @bounced_mail.valid? + @bounced_mail.diagnostic = nil + assert @bounced_mail.valid? + end + + def test_bounce_reason_is_determined_dynamically + assert @bounced_mail.valid? assert_equal 'failed (5.1.1 smtp; 550 5.1.1 user unknown)', @bounced_mail.bounce_reason end - def test_response_json_is_required - assert @bounced_mail.valid? - @bounced_mail.response_json = nil - assert_not @bounced_mail.valid? - assert @bounced_mail.errors.full_messages.include? 'Response json is missing' - end - - def test_recipient_json_is_required - assert @bounced_mail.valid? - @bounced_mail.recipient_json = nil - assert_not @bounced_mail.valid? - - assert @bounced_mail.errors.full_messages.include? 'Recipient json is missing' - end - - def test_status_is_determined_dynamically - assert @bounced_mail.valid? - assert_equal '5.1.1', @bounced_mail.status - @bounced_mail.recipient_json['status'] = 'xxx_status' - assert_equal 'xxx_status', @bounced_mail.status - end - - def test_action_is_determined_dynamically - assert @bounced_mail.valid? - assert_equal 'failed', @bounced_mail.action - @bounced_mail.recipient_json['action'] = 'xxx_action' - assert_equal 'xxx_action', @bounced_mail.action - end - - def test_diagnostic_is_determined_dynamically - assert @bounced_mail.valid? - assert_equal 'smtp; 550 5.1.1 user unknown', @bounced_mail.diagnostic - @bounced_mail.recipient_json['diagnosticCode'] = 'xxx_diagnostic' - assert_equal 'xxx_diagnostic', @bounced_mail.diagnostic - end - def test_creates_objects_from_sns_json BouncedMailAddress.record(sns_bounce_payload) diff --git a/test/system/admin_area/bounced_mail_addresses_test.rb b/test/system/admin_area/bounced_mail_addresses_test.rb index 5500f4375..36b81f0f8 100644 --- a/test/system/admin_area/bounced_mail_addresses_test.rb +++ b/test/system/admin_area/bounced_mail_addresses_test.rb @@ -28,8 +28,7 @@ class AdminBouncedMailAddressesTest < ApplicationSystemTestCase assert_text @bounced_mail.diagnostic assert_text @bounced_mail.email - assert_text 'bouncedRecipients' - assert_text '010f0174a0c7d4f9-27d59756-6111-4d5f-xxxx-26bee0d55fa2-000000' + assert_text @bounced_mail.message_id end def test_deletes_registrar From 574846c626e23b2368c30c3088de9046f36e946b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 2 Oct 2020 16:02:44 +0300 Subject: [PATCH 019/202] Remove unneeded modifications in structure.sql --- db/structure.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/structure.sql b/db/structure.sql index e064d968f..acf134a55 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1743,7 +1743,7 @@ ALTER SEQUENCE public.log_payment_orders_id_seq OWNED BY public.log_payment_orde -- --- Name: log_prices; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: log_prices; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.log_prices ( @@ -1781,7 +1781,7 @@ ALTER SEQUENCE public.log_prices_id_seq OWNED BY public.log_prices.id; -- --- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: - -- CREATE TABLE public.log_registrant_verifications ( @@ -3401,7 +3401,7 @@ ALTER TABLE ONLY public.log_payment_orders -- --- Name: log_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: log_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.log_prices @@ -3409,7 +3409,7 @@ ALTER TABLE ONLY public.log_prices -- --- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.log_registrant_verifications From a33cc40d59567fc0f659d0e8b6a330bb1b2009e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 5 Oct 2020 17:34:36 +0300 Subject: [PATCH 020/202] Extract Contact create BL from EPP controller --- app/controllers/epp/contacts_controller.rb | 12 +++--- app/models/actions/contact_create.rb | 44 ++++++++++++++++++++++ lib/deserializers/xml/contact_create.rb | 23 +++++++++++ 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 app/models/actions/contact_create.rb create mode 100644 lib/deserializers/xml/contact_create.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index df9755af6..5b2323919 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -1,5 +1,5 @@ require 'deserializers/xml/contact_update' - +require 'deserializers/xml/contact_create' module Epp class ContactsController < BaseController before_action :find_contact, only: [:info, :update, :delete] @@ -21,13 +21,13 @@ module Epp def create authorize! :create, Epp::Contact - frame = params[:parsed_frame] - @contact = Epp::Contact.new(frame, current_user.registrar) - @contact.add_legal_file_to_new(frame) - @contact.generate_code + @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) + collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - if @contact.save + action = Actions::ContactCreate.new(@contact, collected_data.legal_document) + + if action.call if !address_processing? && address_given? @response_code = 1100 @response_description = t('epp.contacts.completed_without_address') diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb new file mode 100644 index 000000000..a79876f4e --- /dev/null +++ b/app/models/actions/contact_create.rb @@ -0,0 +1,44 @@ +module Actions + class ContactCreate + attr_reader :contact, :legal_document + + def initialize(contact, legal_document) + @contact = contact + @legal_document = legal_document + end + + def call + maybe_remove_address + maybe_attach_legal_doc + commit + end + + def maybe_remove_address + return if Contact.address_processing? + + contact.city = nil + contact.zip = nil + contact.street = nil + contact.state = nil + contact.country_code = nil + end + + def maybe_attach_legal_doc + return unless legal_document + + doc = LegalDocument.create( + documentable_type: Contact, + document_type: legal_document[:type], body: legal_document[:body] + ) + + contact.legal_documents = [doc] + contact.legal_document_id = doc.id + end + + def commit + contact.generate_code + + contact.save + end + end +end diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb new file mode 100644 index 000000000..7f5cb3709 --- /dev/null +++ b/lib/deserializers/xml/contact_create.rb @@ -0,0 +1,23 @@ +require 'deserializers/xml/legal_document' +require 'deserializers/xml/ident' +require 'deserializers/xml/contact' + +module Deserializers + module Xml + class ContactCreate + attr_reader :frame + + def initialize(frame) + @frame = frame + end + + def contact + @contact ||= ::Deserializers::Xml::Contact.new(frame).call + end + + def legal_document + @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call + end + end + end +end From f72ec0ec3ad6d4e96d6adc76cbabbe8cd964a40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 6 Oct 2020 14:28:57 +0300 Subject: [PATCH 021/202] Add contact create to REPP --- app/api/repp/contact_v1.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 810829ef7..1eb5a88ce 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -30,6 +30,37 @@ module Repp total_number_of_records: current_user.registrar.contacts.count } end + + desc 'Create new contact object' + params do + requires :contact, type: Hash, allow_blank: false do + requires :name, type: String, desc: 'Full name of contact' + requires :ident, type: String, desc: 'Government identifier of contact' + requires :ident_type, type: String, desc: 'Type of contact ident' + requires :ident_country_code, type: String, desc: 'Ident country code' + requires :country_code, type: String, desc: 'Address country' + requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + requires :email, type: String, desc: 'Email address of contact' + requires :fax, type: String, desc: 'Fax number of contact' + requires :street, type: String, desc: 'Address street' + requires :city, type: String, desc: 'Address city' + requires :zip, type: String, desc: 'Address ZIP' + end + end + + post '/' do + @legal_doc = params[:legal_documents] + @contact = Contact.new(params[:contact]) + @contact.registrar = current_user.registrar + action = Actions::ContactCreate.new(@contact, @legal_doc) + + if action.call + @response = { data: { contact: { id: @contact.id } } } + else + status :bad_request + @response = { errors: @contact.errors } + end + end end end end From d4628d52baff1c0b4f3fc68ad00fe9387b803582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 6 Oct 2020 16:02:13 +0300 Subject: [PATCH 022/202] Add basic contact update functionality to REPP --- app/api/repp/contact_v1.rb | 58 ++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 1eb5a88ce..cdcc3ff6f 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -31,7 +31,7 @@ module Repp } end - desc 'Create new contact object' + desc 'Creates a new contact object' params do requires :contact, type: Hash, allow_blank: false do requires :name, type: String, desc: 'Full name of contact' @@ -41,10 +41,14 @@ module Repp requires :country_code, type: String, desc: 'Address country' requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' - requires :fax, type: String, desc: 'Fax number of contact' - requires :street, type: String, desc: 'Address street' - requires :city, type: String, desc: 'Address city' - requires :zip, type: String, desc: 'Address ZIP' + optional :fax, type: String, desc: 'Fax number of contact' + optional :street, type: String, desc: 'Address street' + optional :city, type: String, desc: 'Address city' + optional :zip, type: String, desc: 'Address ZIP' + end + optional :legal_document, type: Hash, allow_blank: false do + requires :body, type: String, desc: 'Raw data of legal document' + requires :type, type: String, desc: 'Format of legal document' end end @@ -55,7 +59,49 @@ module Repp action = Actions::ContactCreate.new(@contact, @legal_doc) if action.call - @response = { data: { contact: { id: @contact.id } } } + @response = { contact: { id: @contact.code } } + else + status :bad_request + @response = { errors: @contact.errors } + end + end + + desc 'Update contact properties' + params do + requires :contact, type: Hash, allow_blank: false do + optional :ident, type: Hash, allow_blank: false do + requires :ident, type: String, desc: 'Government identifier of contact' + requires :ident_type, type: String, desc: 'Type of contact ident' + requires :ident_country_code, type: String, desc: 'Ident country code' + end + optional :name, type: String, desc: 'Full name of contact' + optional :country_code, type: String, desc: 'Address country' + optional :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + optional :email, type: String, desc: 'Email address of contact' + optional :fax, type: String, desc: 'Fax number of contact' + optional :street, type: String, desc: 'Address street' + optional :city, type: String, desc: 'Address city' + optional :zip, type: String, desc: 'Address ZIP' + end + optional :legal_document, type: Hash, allow_blank: false do + requires :body, type: String, desc: 'Raw data of legal document' + requires :type, type: String, desc: 'Format of legal document' + end + end + + post '/:code' do + @contact = current_user.registrar.contacts.find_by(code: params[:code]) + (status(:not_found) && return) unless @contact + + @new_params = params[:contact] + @legal_doc = params[:legal_document] + @ident = params[:contact][:ident] || {} + + action = Actions::ContactUpdate.new(@contact, @new_params, + @legal_doc, @ident, current_user) + + if action.call + @response = {} else status :bad_request @response = { errors: @contact.errors } From 01004b8ed4ee1458f918349d7043ba9cac82b80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 7 Oct 2020 12:46:19 +0300 Subject: [PATCH 023/202] Repp: Contact create: validate identifier --- app/api/repp/contact_v1.rb | 46 ++++++++++++++++------ app/controllers/epp/contacts_controller.rb | 2 +- app/models/actions/contact_create.rb | 27 +++++++++++-- app/models/epp/contact.rb | 7 ++-- lib/deserializers/xml/contact_create.rb | 4 ++ 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index cdcc3ff6f..6a3cae33b 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -34,18 +34,29 @@ module Repp desc 'Creates a new contact object' params do requires :contact, type: Hash, allow_blank: false do + # Contact info requires :name, type: String, desc: 'Full name of contact' - requires :ident, type: String, desc: 'Government identifier of contact' - requires :ident_type, type: String, desc: 'Type of contact ident' - requires :ident_country_code, type: String, desc: 'Ident country code' - requires :country_code, type: String, desc: 'Address country' requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, desc: 'Fax number of contact' - optional :street, type: String, desc: 'Address street' - optional :city, type: String, desc: 'Address city' - optional :zip, type: String, desc: 'Address ZIP' + optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' + + # Ident + requires :ident, type: Hash do + requires :ident, type: String, allow_blank: false, desc: 'Government identifier of contact' + requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' + requires :ident_country_code, type: String, allow_blank: false, desc: 'Ident country code' + end + + # Physical address + optional :addr, type: Hash do + requires :country_code, type: String, allow_blank: false, desc: 'Address country' + requires :street, type: String, allow_blank: false, desc: 'Address street' + requires :city, type: String, allow_blank: false, desc: 'Address city' + requires :zip, type: String, allow_blank: false, desc: 'Address ZIP' + end end + + # Legal document optional :legal_document, type: Hash, allow_blank: false do requires :body, type: String, desc: 'Raw data of legal document' requires :type, type: String, desc: 'Format of legal document' @@ -54,9 +65,20 @@ module Repp post '/' do @legal_doc = params[:legal_documents] - @contact = Contact.new(params[:contact]) - @contact.registrar = current_user.registrar - action = Actions::ContactCreate.new(@contact, @legal_doc) + @contact_params = params[:contact] + + # Ident object + @ident = @contact_params[:ident] + @contact_params.delete(:ident) + + # Address + %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } + @contact_params.delete(:addr) + + @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + puts "#{params[:contact]}" + + action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) if action.call @response = { contact: { id: @contact.code } } @@ -89,7 +111,7 @@ module Repp end end - post '/:code' do + put '/:code' do @contact = current_user.registrar.contacts.find_by(code: params[:code]) (status(:not_found) && return) unless @contact diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 5b2323919..cfaaeb89e 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -25,7 +25,7 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document) + action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) if action.call if !address_processing? && address_given? diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index a79876f4e..4cb6607ae 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -1,15 +1,17 @@ module Actions class ContactCreate - attr_reader :contact, :legal_document + attr_reader :contact, :legal_document, :ident - def initialize(contact, legal_document) + def initialize(contact, legal_document, ident) @contact = contact @legal_document = legal_document + @ident = ident end def call maybe_remove_address maybe_attach_legal_doc + validate_ident commit end @@ -23,6 +25,24 @@ module Actions contact.country_code = nil end + def validate_ident + if ident.present? && ident[:ident_type].blank? + contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + end + + if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? + contact.add_epp_error('2003', nil, 'ident_country_code', I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + end + + identifier = ::Contact::Ident.new(code: ident[:ident], type: ident[:ident_type], + country_code: ident[:ident_country_code]) + + identifier.validate + contact.identifier = identifier + end + def maybe_attach_legal_doc return unless legal_document @@ -36,8 +56,9 @@ module Actions end def commit - contact.generate_code + return false if @error + contact.generate_code contact.save end end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 6867b037d..50ebac065 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -30,12 +30,13 @@ class Epp::Contact < Contact at end - def new(frame, registrar) + def new(frame, registrar, epp: true) return super if frame.blank? + attrs = epp ? attrs_from(frame, new_record: true) : frame super( - attrs_from(frame, new_record: true).merge( - code: frame.css('id').text, + attrs.merge( + code: epp ? frame.css('id').text : frame[:id], registrar: registrar ) ) diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb index 7f5cb3709..5bd9c768a 100644 --- a/lib/deserializers/xml/contact_create.rb +++ b/lib/deserializers/xml/contact_create.rb @@ -18,6 +18,10 @@ module Deserializers def legal_document @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call end + + def ident + @ident ||= ::Deserializers::Xml::Ident.new(frame).call + end end end end From dc8551807a24f6d4d6550c8933e60206c9f4e76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 7 Oct 2020 14:17:53 +0300 Subject: [PATCH 024/202] Add error structuring to Grape API --- app/api/repp/api.rb | 8 ++++++ app/api/repp/contact_v1.rb | 31 ++++++++++++++++------ app/controllers/epp/contacts_controller.rb | 1 - 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb index af6864cfa..f1908f81e 100644 --- a/app/api/repp/api.rb +++ b/app/api/repp/api.rb @@ -3,6 +3,14 @@ module Repp format :json prefix :repp + rescue_from Grape::Exceptions::ValidationErrors do |e| + messages = e.full_messages + errors = [] + messages.each { |m| errors << { code: 2003, message: m } } + + error!({ errors: errors }, 400) + end + http_basic do |username, password| @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) if @current_user diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb index 6a3cae33b..85065f5a0 100644 --- a/app/api/repp/contact_v1.rb +++ b/app/api/repp/contact_v1.rb @@ -36,15 +36,18 @@ module Repp requires :contact, type: Hash, allow_blank: false do # Contact info requires :name, type: String, desc: 'Full name of contact' - requires :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + requires :phone, type: String, + desc: 'Phone number of contact. In format of +country_prefix.number' requires :email, type: String, desc: 'Email address of contact' optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' # Ident requires :ident, type: Hash do - requires :ident, type: String, allow_blank: false, desc: 'Government identifier of contact' + requires :ident, type: String, allow_blank: false, + desc: 'Government identifier of contact' requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' - requires :ident_country_code, type: String, allow_blank: false, desc: 'Ident country code' + requires :ident_country_code, type: String, allow_blank: false, + desc: 'Ident country code' end # Physical address @@ -72,18 +75,29 @@ module Repp @contact_params.delete(:ident) # Address + address_present = params[:contact][:addr].keys.any? + %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } @contact_params.delete(:addr) @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) - puts "#{params[:contact]}" action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) if action.call - @response = { contact: { id: @contact.code } } + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + @response = { code: @response_code, + description: @response_description, + data: { contact: { id: @contact.code } } } else - status :bad_request + status(:bad_request) @response = { errors: @contact.errors } end end @@ -98,7 +112,8 @@ module Repp end optional :name, type: String, desc: 'Full name of contact' optional :country_code, type: String, desc: 'Address country' - optional :phone, type: String, desc: 'Phone number of contact. In format of +country_prefix.number' + optional :phone, type: String, + desc: 'Phone number of contact. In format of +country_prefix.number' optional :email, type: String, desc: 'Email address of contact' optional :fax, type: String, desc: 'Fax number of contact' optional :street, type: String, desc: 'Address street' @@ -125,7 +140,7 @@ module Repp if action.call @response = {} else - status :bad_request + status(:bad_request) @response = { errors: @contact.errors } end end diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index cfaaeb89e..b674d0919 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -24,7 +24,6 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) if action.call From aa325604f9673f18aa2d92803723807bc8c93943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 8 Oct 2020 15:54:23 +0300 Subject: [PATCH 025/202] Port REPP Contacts from Grape to Rails internal API --- app/api/repp/contact_v1.rb | 149 ------------------ app/controllers/repp/v1/base_controller.rb | 82 ++++++++++ .../repp/v1/contacts_controller.rb | 111 +++++++++++++ config/routes.rb | 3 +- 4 files changed, 194 insertions(+), 151 deletions(-) delete mode 100644 app/api/repp/contact_v1.rb create mode 100644 app/controllers/repp/v1/base_controller.rb create mode 100644 app/controllers/repp/v1/contacts_controller.rb diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb deleted file mode 100644 index 85065f5a0..000000000 --- a/app/api/repp/contact_v1.rb +++ /dev/null @@ -1,149 +0,0 @@ -module Repp - class ContactV1 < Grape::API - version 'v1', using: :path - - resource :contacts do - desc 'Return list of contact' - params do - optional :limit, type: Integer, values: (1..200).to_a, desc: 'How many contacts to show' - optional :offset, type: Integer, desc: 'Contact number to start at' - optional :details, type: String, values: %w(true false), desc: 'Whether to include details' - end - - get '/' do - limit = params[:limit] || 200 - offset = params[:offset] || 0 - - if params[:details] == 'true' - contacts = current_user.registrar.contacts.limit(limit).offset(offset) - - unless Contact.address_processing? - attributes = Contact.attribute_names - Contact.address_attribute_names - contacts = contacts.select(attributes) - end - else - contacts = current_user.registrar.contacts.limit(limit).offset(offset).pluck(:code) - end - - @response = { - contacts: contacts, - total_number_of_records: current_user.registrar.contacts.count - } - end - - desc 'Creates a new contact object' - params do - requires :contact, type: Hash, allow_blank: false do - # Contact info - requires :name, type: String, desc: 'Full name of contact' - requires :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - requires :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' - - # Ident - requires :ident, type: Hash do - requires :ident, type: String, allow_blank: false, - desc: 'Government identifier of contact' - requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' - requires :ident_country_code, type: String, allow_blank: false, - desc: 'Ident country code' - end - - # Physical address - optional :addr, type: Hash do - requires :country_code, type: String, allow_blank: false, desc: 'Address country' - requires :street, type: String, allow_blank: false, desc: 'Address street' - requires :city, type: String, allow_blank: false, desc: 'Address city' - requires :zip, type: String, allow_blank: false, desc: 'Address ZIP' - end - end - - # Legal document - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - post '/' do - @legal_doc = params[:legal_documents] - @contact_params = params[:contact] - - # Ident object - @ident = @contact_params[:ident] - @contact_params.delete(:ident) - - # Address - address_present = params[:contact][:addr].keys.any? - - %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } - @contact_params.delete(:addr) - - @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) - - action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) - - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end - - @response = { code: @response_code, - description: @response_description, - data: { contact: { id: @contact.code } } } - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - - desc 'Update contact properties' - params do - requires :contact, type: Hash, allow_blank: false do - optional :ident, type: Hash, allow_blank: false do - requires :ident, type: String, desc: 'Government identifier of contact' - requires :ident_type, type: String, desc: 'Type of contact ident' - requires :ident_country_code, type: String, desc: 'Ident country code' - end - optional :name, type: String, desc: 'Full name of contact' - optional :country_code, type: String, desc: 'Address country' - optional :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - optional :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, desc: 'Fax number of contact' - optional :street, type: String, desc: 'Address street' - optional :city, type: String, desc: 'Address city' - optional :zip, type: String, desc: 'Address ZIP' - end - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - put '/:code' do - @contact = current_user.registrar.contacts.find_by(code: params[:code]) - (status(:not_found) && return) unless @contact - - @new_params = params[:contact] - @legal_doc = params[:legal_document] - @ident = params[:contact][:ident] || {} - - action = Actions::ContactUpdate.new(@contact, @new_params, - @legal_doc, @ident, current_user) - - if action.call - @response = {} - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb new file mode 100644 index 000000000..30e37b9f7 --- /dev/null +++ b/app/controllers/repp/v1/base_controller.rb @@ -0,0 +1,82 @@ +module Repp + module V1 + class BaseController < ActionController::API + rescue_from ActiveRecord::RecordNotFound, with: :not_found_error + before_action :authenticate_user + before_action :check_ip_restriction + + attr_reader :current_user + + rescue_from ActionController::ParameterMissing do |exception| + render json: { code: 2003, message: exception }, status: :bad_request + end + + private + + def epp_errors + @errors ||= [] + end + + def handle_errors(obj = nil, update: false) + @errors ||= [] + + if obj + obj.construct_epp_errors + @errors += obj.errors[:epp_errors] + end + + if 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_error + end + + def render_epp_error + render(json: { code: @errors[0][:code], message: @errors[0][:msg] }, status: :bad_request) + end + + def ip_whitelisted? + return false unless @api_user.registrar.api_ip_white?(request.ip) + end + + def basic_token + pattern = /^Basic / + header = request.headers['Authorization'] + header.gsub(pattern, '') if header&.match(pattern) + end + + def authenticate_user + username, password = Base64.urlsafe_decode64(basic_token).split(':') + @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) + + return if @current_user + + render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + end + + def check_ip_restriction + ip_restriction = Authorization::RestrictedIP.new(request.ip) + allowed = ip_restriction.can_access_registrar_area?(@current_user.registrar) + + return if allowed + + flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) + render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) + end + + def not_found_error + render(json: { code: 2303, message: 'Object does not exist' }, status: :not_found) + end + end + end +end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb new file mode 100644 index 000000000..70bf91297 --- /dev/null +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -0,0 +1,111 @@ +module Repp + module V1 + class ContactsController < BaseController + before_action :find_contact, only: [:update] + + ## GET /repp/v1/contacts + def index + limit = params[:limit] || 200 + offset = params[:offset] || 0 + + record_count = current_user.registrar.contacts.count + contacts = current_user.registrar.contacts.limit(limit).offset(offset) + + unless Contact.address_processing? && params[:details] == 'true' + contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) + end + + contacts = contacts.pluck(:code) unless params[:details] + resp = { contacts: contacts, total_number_of_records: record_count } + render(json: resp, status: :ok) + end + + ## POST /repp/v1/contacts + def create + @legal_doc = params[:legal_documents] + @contact_params = contact_create_params + @ident = contact_ident_params + address_present = contact_addr_params.keys.any? + %w[city street zip country_code].each { |k| @contact_params[k] = contact_addr_params[k] } + + @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + + action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :created) + else + handle_errors(@contact) + end + end + + ## PUT /repp/v1/contacts/1 + def update + @update = contact_create_params + %w[city street zip country_code].each { |k| @new_params[k] = contact_addr_params[k] } + + @legal_doc = params[:legal_document] + @ident = contact_ident_params || {} + address_present = contact_addr_params.keys.any? + action = Actions::ContactUpdate.new(@contact, @update, @legal_doc, @ident, current_user) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :ok) + else + handle_errors(@contact) + end + end + + def find_contact + code = params[:id] + @contact = Epp::Contact.find_by!(code: code) + end + + def contact_create_params + params.require(:contact).require(%i[name email phone]) + params.require(:contact).permit(:name, :email, :phone) + end + + def contact_ident_params + params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) + params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + end + + def contact_addr_params + if Contact.address_processing? + params.require(:contact).require(:addr).require(%i[country_code city street zip]) + params.require(:contact).require(:addr).permit(:country_code, :city, :street, :zip) + else + params.require(:contact).permit(addr: %i[country_code city street zip]) + end + end + + def legal_document_params + params.require(:legal_document).require(%i[body type]) + params.require(:legal_document).permit(:body, :type) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 41f857bc8..afad7b947 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,10 +37,9 @@ Rails.application.routes.draw do get 'error/:command', to: 'errors#error' end - mount Repp::API => '/' - namespace :repp do namespace :v1 do + resources :contacts resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] end From 1686c352bced9bb5bea9499729fa3b72797b59df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 8 Oct 2020 16:28:05 +0300 Subject: [PATCH 026/202] REPP: Port accounts controller from Grape to Rails API --- app/api/repp/account_v1.rb | 16 ---------------- app/controllers/repp/v1/accounts_controller.rb | 11 +++++++++++ config/routes.rb | 5 +++++ 3 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 app/api/repp/account_v1.rb create mode 100644 app/controllers/repp/v1/accounts_controller.rb diff --git a/app/api/repp/account_v1.rb b/app/api/repp/account_v1.rb deleted file mode 100644 index fe3acd387..000000000 --- a/app/api/repp/account_v1.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Repp - class AccountV1 < Grape::API - version 'v1', using: :path - - resource :accounts do - desc 'Return current cash account balance' - - get 'balance' do - @response = { - balance: current_user.registrar.cash_account.balance, - currency: current_user.registrar.cash_account.currency - } - end - end - end -end diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb new file mode 100644 index 000000000..5adf99ea1 --- /dev/null +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -0,0 +1,11 @@ +module Repp + module V1 + class AccountsController < BaseController + def balance + resp = { balance: current_user.registrar.cash_account.balance, + currency: current_user.registrar.cash_account.currency } + render(json: resp, status: :ok) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index afad7b947..5c518a533 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,6 +40,11 @@ Rails.application.routes.draw do namespace :repp do namespace :v1 do resources :contacts + resources :accounts do + collection do + get 'balance' + end + end resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] end From 39c6d204134375fd04765c1b10b4ce11402a2779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 09:34:59 +0300 Subject: [PATCH 027/202] Repp: Add contacts/check action --- .../repp/v1/contacts_controller.rb | 20 ++++++++++++++++++- config/routes.rb | 7 ++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 70bf91297..1d2dbd441 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,7 +1,7 @@ module Repp module V1 class ContactsController < BaseController - before_action :find_contact, only: [:update] + before_action :find_contact, only: %i[show update] ## GET /repp/v1/contacts def index @@ -20,6 +20,24 @@ module Repp render(json: resp, status: :ok) end + ## GET /repp/v1/contacts/1 + def show + render(json: @contact.as_json, status: :ok) + end + + ## GET /repp/v1/contacts/check/1 + def check + contact = Epp::Contact.find_by(code: params[:id]) + + render json: { + code: 1000, message: I18n.t('epp.contacts.completed'), + data: { contact: { + id: params[:id], + available: contact.nil? + } } + }, status: :ok + end + ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] diff --git a/config/routes.rb b/config/routes.rb index 5c518a533..634b40e31 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -39,7 +39,12 @@ Rails.application.routes.draw do namespace :repp do namespace :v1 do - resources :contacts + resources :contacts do + collection do + get 'check/:id', to: 'contacts#check' + end + end + resources :accounts do collection do get 'balance' From a73cd53bff04f28c87030cbf064dfe3f937c77fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 11:12:25 +0300 Subject: [PATCH 028/202] REPP: Verify contact ident type --- app/controllers/repp/v1/base_controller.rb | 3 +-- app/controllers/repp/v1/contacts_controller.rb | 12 ++++++++---- app/models/actions/contact_create.rb | 11 ++++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 30e37b9f7..f1000849f 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -46,7 +46,7 @@ module Repp end def ip_whitelisted? - return false unless @api_user.registrar.api_ip_white?(request.ip) + return false unless current_user.registrar.api_ip_white?(request.ip) end def basic_token @@ -70,7 +70,6 @@ module Repp return if allowed - flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 1d2dbd441..51a03f8d4 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -41,10 +41,9 @@ module Repp ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] - @contact_params = contact_create_params + @contact_params = contact_params_with_address @ident = contact_ident_params address_present = contact_addr_params.keys.any? - %w[city street zip country_code].each { |k| @contact_params[k] = contact_addr_params[k] } @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) @@ -70,8 +69,7 @@ module Repp ## PUT /repp/v1/contacts/1 def update - @update = contact_create_params - %w[city street zip country_code].each { |k| @new_params[k] = contact_addr_params[k] } + @update = contact_params_with_address @legal_doc = params[:legal_document] @ident = contact_ident_params || {} @@ -101,6 +99,12 @@ module Repp @contact = Epp::Contact.find_by!(code: code) end + def contact_params_with_address + addr = {} + contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } + contact_create_params.merge(addr) + end + def contact_create_params params.require(:contact).require(%i[name email phone]) params.require(:contact).permit(:name, :email, :phone) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 4cb6607ae..095c3f61f 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -26,9 +26,14 @@ module Actions end def validate_ident - if ident.present? && ident[:ident_type].blank? - contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) - @error = true + if ident.present? + if ident[:ident_type].blank? + contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + elsif !%w[priv org birthday].include?(ident[:ident_type]) + contact.add_epp_error('2003', nil, 'ident_type', 'Invalid ident type') + @error = true + end end if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? From 2e8ce7dbc4c23e31afd64e4365089943866d4789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 12:00:57 +0300 Subject: [PATCH 029/202] REPP: Allow contact creation without address --- app/controllers/repp/v1/contacts_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 51a03f8d4..70f5be1b5 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -101,6 +101,8 @@ module Repp def contact_params_with_address addr = {} + return contact_create_params unless contact_addr_params.key?(:addr) + contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } contact_create_params.merge(addr) end From f6580bd79a6e4bf8d1d7579a84f84c8420b70ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 9 Oct 2020 13:10:28 +0300 Subject: [PATCH 030/202] REPP: custom success response handler --- app/controllers/repp/v1/base_controller.rb | 7 ++ .../repp/v1/contacts_controller.rb | 85 +++++++------------ 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index f1000849f..81f812761 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -13,6 +13,13 @@ module Repp private + def render_success(code: nil, message: nil, data: nil) + resp = { code: code || 1000, message: message || 'Command completed successfully', + data: data || {} } + + render(json: resp, status: :ok) + end + def epp_errors @errors ||= [] end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 70f5be1b5..66ab3712f 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -22,76 +22,49 @@ module Repp ## GET /repp/v1/contacts/1 def show - render(json: @contact.as_json, status: :ok) + render_success(data: @contact.as_json) end ## GET /repp/v1/contacts/check/1 def check contact = Epp::Contact.find_by(code: params[:id]) + data = { contact: { id: params[:id], available: contact.nil? } } - render json: { - code: 1000, message: I18n.t('epp.contacts.completed'), - data: { contact: { - id: params[:id], - available: contact.nil? - } } - }, status: :ok + render_success(data: data) end ## POST /repp/v1/contacts def create @legal_doc = params[:legal_documents] - @contact_params = contact_params_with_address - @ident = contact_ident_params - address_present = contact_addr_params.keys.any? - @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) + action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) + handle_errors(@contact) and return unless action.call - action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) - - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end - - render(json: { code: @response_code, - message: @response_description, - data: { contact: { id: @contact.code } } }, - status: :created) - else - handle_errors(@contact) - end + render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) end ## PUT /repp/v1/contacts/1 def update - @update = contact_params_with_address + action = Actions::ContactUpdate.new(@contact, contact_params_with_address, + params[:legal_document], + contact_ident_params(required: false), current_user) - @legal_doc = params[:legal_document] - @ident = contact_ident_params || {} - address_present = contact_addr_params.keys.any? - action = Actions::ContactUpdate.new(@contact, @update, @legal_doc, @ident, current_user) + handle_errors(@contact) and return unless action.call - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end + render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + end - render(json: { code: @response_code, - message: @response_description, - data: { contact: { id: @contact.code } } }, - status: :ok) - else - handle_errors(@contact) - end + def contact_addr_present? + return false unless contact_addr_params.key?(:addr) + + contact_addr_params[:addr].keys.any? + end + + def opt_addr? + !Contact.address_processing? && contact_addr_present? end def find_contact @@ -100,9 +73,9 @@ module Repp end def contact_params_with_address - addr = {} return contact_create_params unless contact_addr_params.key?(:addr) + addr = {} contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } contact_create_params.merge(addr) end @@ -112,9 +85,13 @@ module Repp params.require(:contact).permit(:name, :email, :phone) end - def contact_ident_params - params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) - params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + def contact_ident_params(required: true) + if required + params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) + params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + else + params.permit(ident: %i[ident ident_type ident_country_code]) + end end def contact_addr_params From eb19412643754f060f6db1c13ded02880cf037b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 12:33:15 +0300 Subject: [PATCH 031/202] Fix some CC issues --- app/controllers/epp/contacts_controller.rb | 3 ++- app/controllers/repp/v1/base_controller.rb | 18 +++++++++++------- app/models/actions/contact_create.rb | 6 ++++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index b674d0919..66791abc5 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -24,7 +24,8 @@ module Epp @contact = Epp::Contact.new(params[:parsed_frame], current_user.registrar) collected_data = ::Deserializers::Xml::ContactCreate.new(params[:parsed_frame]) - action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) + action = Actions::ContactCreate.new(@contact, collected_data.legal_document, + collected_data.ident) if action.call if !address_processing? && address_given? diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 81f812761..45144ca5a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -34,12 +34,11 @@ module Repp if 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 + next unless 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 @@ -77,7 +76,12 @@ module Repp return if allowed - render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) + render( + status: :unauthorized, + json: { errors: [ + { base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }, + ] } + ) end def not_found_error diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 095c3f61f..ff46ed817 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -28,7 +28,8 @@ module Actions def validate_ident if ident.present? if ident[:ident_type].blank? - contact.add_epp_error('2003', nil, 'ident_type', I18n.t('errors.messages.required_ident_attribute_missing')) + contact.add_epp_error('2003', nil, 'ident_type', + I18n.t('errors.messages.required_ident_attribute_missing')) @error = true elsif !%w[priv org birthday].include?(ident[:ident_type]) contact.add_epp_error('2003', nil, 'ident_type', 'Invalid ident type') @@ -37,7 +38,8 @@ module Actions end if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? - contact.add_epp_error('2003', nil, 'ident_country_code', I18n.t('errors.messages.required_ident_attribute_missing')) + contact.add_epp_error('2003', nil, 'ident_country_code', + I18n.t('errors.messages.required_ident_attribute_missing')) @error = true end From 5cd495b85cdde28cea5a1820ecb0daa824468a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:07:41 +0300 Subject: [PATCH 032/202] REPP: Simplify Contacts#index method --- .../repp/v1/contacts_controller.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 66ab3712f..900c2140d 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -9,13 +9,9 @@ module Repp offset = params[:offset] || 0 record_count = current_user.registrar.contacts.count - contacts = current_user.registrar.contacts.limit(limit).offset(offset) + show_addresses = Contact.address_processing? && params[:details] == 'true' + contacts = showable_contacts(params[:details], limit, offset, show_addresses) - unless Contact.address_processing? && params[:details] == 'true' - contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) - end - - contacts = contacts.pluck(:code) unless params[:details] resp = { contacts: contacts, total_number_of_records: record_count } render(json: resp, status: :ok) end @@ -63,6 +59,17 @@ module Repp contact_addr_params[:addr].keys.any? end + def showable_contacts(details, limit, offset, addresses) + contacts = current_user.registrar.contacts.limit(limit).offset(offset) + unless addresses + contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) + end + + contacts = contacts.pluck(:code) unless details + + contacts + end + def opt_addr? !Contact.address_processing? && contact_addr_present? end From 737939082799ffa4451ee1a5bf1e335f836d9cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:22:42 +0300 Subject: [PATCH 033/202] Fix whining about AndOr --- app/controllers/repp/v1/contacts_controller.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 900c2140d..e7e5c73cf 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -35,7 +35,10 @@ module Repp @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) - handle_errors(@contact) and return unless action.call + unless action.call + handle_errors(@contact) + return + end render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) @@ -47,7 +50,10 @@ module Repp params[:legal_document], contact_ident_params(required: false), current_user) - handle_errors(@contact) and return unless action.call + unless action.call + handle_errors(@contact) + return + end render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) From 8755fbdbf47866d915b9c8e11a9e30aa53d530c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 14:55:44 +0300 Subject: [PATCH 034/202] REPP: compose success data in new method --- .../repp/v1/contacts_controller.rb | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index e7e5c73cf..b70e67c61 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -5,15 +5,12 @@ module Repp ## GET /repp/v1/contacts def index - limit = params[:limit] || 200 - offset = params[:offset] || 0 - record_count = current_user.registrar.contacts.count show_addresses = Contact.address_processing? && params[:details] == 'true' - contacts = showable_contacts(params[:details], limit, offset, show_addresses) + contacts = showable_contacts(params[:details], params[:limit] || 200, + params[:offset] || 0, show_addresses) - resp = { contacts: contacts, total_number_of_records: record_count } - render(json: resp, status: :ok) + render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) end ## GET /repp/v1/contacts/1 @@ -31,17 +28,16 @@ module Repp ## POST /repp/v1/contacts def create - @legal_doc = params[:legal_documents] - @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) - action = Actions::ContactCreate.new(@contact, @legal_doc, contact_ident_params) + action = Actions::ContactCreate.new(@contact, params[:legal_documents], + contact_ident_params) + unless action.call handle_errors(@contact) return end - render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, - message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + render_success(create_update_success_data) end ## PUT /repp/v1/contacts/1 @@ -55,8 +51,7 @@ module Repp return end - render_success(code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, - message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil) + render_success(create_update_success_data) end def contact_addr_present? @@ -65,6 +60,11 @@ module Repp contact_addr_params[:addr].keys.any? end + def create_update_success_body + { code: opt_addr? ? 1100 : nil, data: { contact: { id: @contact.code } }, + message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil } + end + def showable_contacts(details, limit, offset, addresses) contacts = current_user.registrar.contacts.limit(limit).offset(offset) unless addresses From a495c2ab1c37efc4f292abc0eb60bb9b93486aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 15:28:31 +0300 Subject: [PATCH 035/202] Fix CC issues --- app/controllers/repp/v1/base_controller.rb | 17 ++++++++++------- app/controllers/repp/v1/contacts_controller.rb | 7 +++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 45144ca5a..5116a2238 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -21,34 +21,37 @@ module Repp end def epp_errors - @errors ||= [] + @epp_errors ||= [] end def handle_errors(obj = nil, update: false) - @errors ||= [] + @epp_errors ||= [] if obj obj.construct_epp_errors - @errors += obj.errors[:epp_errors] + @epp_errors += obj.errors[:epp_errors] end if update - @errors.each_with_index do |errors, index| + @epp_errors.each_with_index do |errors, index| next unless errors[:code] == '2304' && errors[:value].present? && errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && errors[:value][:obj] == 'status' - @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE end end - @errors.uniq! + @epp_errors.uniq! render_epp_error end def render_epp_error - render(json: { code: @errors[0][:code], message: @errors[0][:msg] }, status: :bad_request) + render( + json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, + status: :bad_request + ) end def ip_whitelisted? diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index b70e67c61..34099bd93 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -6,9 +6,8 @@ module Repp ## GET /repp/v1/contacts def index record_count = current_user.registrar.contacts.count - show_addresses = Contact.address_processing? && params[:details] == 'true' contacts = showable_contacts(params[:details], params[:limit] || 200, - params[:offset] || 0, show_addresses) + params[:offset] || 0) render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) end @@ -65,9 +64,9 @@ module Repp message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil } end - def showable_contacts(details, limit, offset, addresses) + def showable_contacts(details, limit, offset) contacts = current_user.registrar.contacts.limit(limit).offset(offset) - unless addresses + unless Contact.address_processing? && params[:details] == 'true' contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) end From b0f9d316c94a7968c50892669e715fb480fc9339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 12 Oct 2020 16:26:08 +0300 Subject: [PATCH 036/202] REPP: Refactor handle_errors() --- app/controllers/repp/v1/base_controller.rb | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 5116a2238..714fb3a47 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -27,26 +27,25 @@ module Repp def handle_errors(obj = nil, update: false) @epp_errors ||= [] - if obj - obj.construct_epp_errors - @epp_errors += obj.errors[:epp_errors] - end - - if update - @epp_errors.each_with_index do |errors, index| - next unless errors[:code] == '2304' && errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' - - @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE - end - end + obj&.construct_epp_errors + @epp_errors += obj.errors[:epp_errors] if obj + format_epp_errors if update @epp_errors.uniq! render_epp_error end + def format_epp_errors + @epp_errors.each_with_index do |errors, index| + next unless errors[:code] == '2304' && errors[:value].present? && + errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + errors[:value][:obj] == 'status' + + @epp_errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + end + end + def render_epp_error render( json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, From 3e27869379f72e4a678f537656c735a1979a57cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 11:00:06 +0300 Subject: [PATCH 037/202] Reuse action call response code --- app/controllers/epp/contacts_controller.rb | 42 +++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 66791abc5..adaa26070 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -27,19 +27,7 @@ module Epp action = Actions::ContactCreate.new(@contact, collected_data.legal_document, collected_data.ident) - if action.call - if !address_processing? && address_given? - @response_code = 1100 - @response_description = t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = t('epp.contacts.completed') - end - - render_epp_response '/epp/contacts/save' - else - handle_errors(@contact) - end + action_call_response(action: action) end def update @@ -52,19 +40,7 @@ module Epp collected_data.ident, current_user) - if action.call - if !address_processing? && address_given? - @response_code = 1100 - @response_description = t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = t('epp.contacts.completed') - end - - render_epp_response 'epp/contacts/save' - else - handle_errors(@contact) - end + action_call_response(action: action) end def delete @@ -91,6 +67,20 @@ module Epp private + def action_call_response(action:) + unless action.call + handle_errors(@contact) + return + end + + @response_code = !address_processing? && address_given? ? 1100 : 1000 + str = 'epp.contacts.completed' + str = "#{str}_without_address" if !address_processing? && address_given? + + @response_description = t(str) + render_epp_response('epp/contacts/save') + end + def find_password @password = params[:parsed_frame].css('authInfo pw').text end From c59b47a519e2278c522146cc1aff7f7daa32a509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 11:20:07 +0300 Subject: [PATCH 038/202] REPP: Simplify ident validation --- app/models/actions/contact_create.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index ff46ed817..a771dbde4 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -26,16 +26,7 @@ module Actions end def validate_ident - if ident.present? - if ident[:ident_type].blank? - contact.add_epp_error('2003', nil, 'ident_type', - I18n.t('errors.messages.required_ident_attribute_missing')) - @error = true - elsif !%w[priv org birthday].include?(ident[:ident_type]) - contact.add_epp_error('2003', nil, 'ident_type', 'Invalid ident type') - @error = true - end - end + validate_ident_integrity if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? contact.add_epp_error('2003', nil, 'ident_country_code', @@ -50,6 +41,19 @@ module Actions contact.identifier = identifier end + def validate_ident_integrity + return if ident.blank? + + if ident[:ident_type].blank? + contact.add_epp_error('2003', nil, 'ident_type', + I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + elsif !%w[priv org birthday].include?(ident[:ident_type]) + contact.add_epp_error('2003', nil, 'ident_type', 'Invalid ident type') + @error = true + end + end + def maybe_attach_legal_doc return unless legal_document From cdf28befca91078a4385214ab5da3b61e97eb26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 13:19:57 +0300 Subject: [PATCH 039/202] Fix CC issues --- app/controllers/epp/contacts_controller.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index adaa26070..c28d5256b 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -67,17 +67,21 @@ module Epp private + def opt_addr? + !address_processing? && address_given? + end + def action_call_response(action:) - unless action.call - handle_errors(@contact) - return + (handle_errors(@contact) and return) unless action.call + + if opt_addr? + @response_code = 1100 + @response_description = t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = t('epp.contacts.completed') end - @response_code = !address_processing? && address_given? ? 1100 : 1000 - str = 'epp.contacts.completed' - str = "#{str}_without_address" if !address_processing? && address_given? - - @response_description = t(str) render_epp_response('epp/contacts/save') end From aac74e26f1cabf1ff33952378beaf615a908322a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 13:33:39 +0300 Subject: [PATCH 040/202] Fix some more CC issues --- app/controllers/epp/contacts_controller.rb | 12 ++++-------- app/models/actions/contact_create.rb | 16 ++++++++++------ app/views/epp/contacts/info.xml.builder | 4 ++-- app/views/registrar/contacts/_form.haml | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index c28d5256b..4ef8d11d3 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -4,7 +4,6 @@ module Epp class ContactsController < BaseController before_action :find_contact, only: [:info, :update, :delete] before_action :find_password, only: [:info, :update, :delete] - helper_method :address_processing? def info authorize! :info, @contact, @password @@ -68,11 +67,13 @@ module Epp private def opt_addr? - !address_processing? && address_given? + !Contact.address_processing? && address_given? end def action_call_response(action:) + # rubocop:disable Style/AndOr (handle_errors(@contact) and return) unless action.call + # rubocop:enable Style/AndOr if opt_addr? @response_code = 1100 @@ -123,8 +124,7 @@ module Epp 'postalInfo > addr > cc', ] - required_attributes.concat(address_attributes) if address_processing? - + required_attributes.concat(address_attributes) if Contact.address_processing? requires(*required_attributes) ident = params[:parsed_frame].css('ident') @@ -200,9 +200,5 @@ module Epp def address_given? params[:parsed_frame].css('postalInfo addr').size != 0 end - - def address_processing? - Contact.address_processing? - end end end diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index a771dbde4..13162a553 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -27,12 +27,7 @@ module Actions def validate_ident validate_ident_integrity - - if ident.present? && ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? - contact.add_epp_error('2003', nil, 'ident_country_code', - I18n.t('errors.messages.required_ident_attribute_missing')) - @error = true - end + validate_ident_birthday identifier = ::Contact::Ident.new(code: ident[:ident], type: ident[:ident_type], country_code: ident[:ident_country_code]) @@ -54,6 +49,15 @@ module Actions end end + def validate_ident_birthday + return if ident.blank? + return unless ident[:ident_type] != 'birthday' && ident[:ident_country_code].blank? + + contact.add_epp_error('2003', nil, 'ident_country_code', + I18n.t('errors.messages.required_ident_attribute_missing')) + @error = true + end + def maybe_attach_legal_doc return unless legal_document diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 1945e7def..776f7ceb2 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -18,7 +18,7 @@ xml.epp_head do if can? :view_full_info, @contact, @password xml.tag!('contact:org', @contact.org_name) if @contact.org_name.present? - if address_processing? + if Contact.address_processing? xml.tag!('contact:addr') do xml.tag!('contact:street', @contact.street) xml.tag!('contact:city', @contact.city) @@ -31,7 +31,7 @@ xml.epp_head do else xml.tag!('contact:org', 'No access') - if address_processing? + if Contact.address_processing? xml.tag!('contact:addr') do xml.tag!('contact:street', 'No access') xml.tag!('contact:city', 'No access') diff --git a/app/views/registrar/contacts/_form.haml b/app/views/registrar/contacts/_form.haml index cf8217e13..953c502e5 100644 --- a/app/views/registrar/contacts/_form.haml +++ b/app/views/registrar/contacts/_form.haml @@ -5,7 +5,7 @@ .col-md-8 = render 'registrar/contacts/form/general', f: f -- if address_processing? +- if Contact.address_processing? .row .col-md-8 = render 'registrar/contacts/form/address', f: f From a782b19d284c70dea7535b91b2b6414b60a80a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 14:43:45 +0300 Subject: [PATCH 041/202] Deserializers: Mirror ContactCreate from ContactUpdate --- app/controllers/repp/v1/base_controller.rb | 12 ++++++++---- .../repp/v1/contacts_controller.rb | 4 ++-- lib/deserializers/xml/contact_create.rb | 19 +------------------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 714fb3a47..3cd636eb2 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -37,10 +37,14 @@ module Repp end def format_epp_errors - @epp_errors.each_with_index do |errors, index| - next unless errors[:code] == '2304' && errors[:value].present? && - errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && - errors[:value][:obj] == 'status' + @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 diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 34099bd93..6321f2a40 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -36,7 +36,7 @@ module Repp return end - render_success(create_update_success_data) + render_success(create_update_success_body) end ## PUT /repp/v1/contacts/1 @@ -50,7 +50,7 @@ module Repp return end - render_success(create_update_success_data) + render_success(create_update_success_body) end def contact_addr_present? diff --git a/lib/deserializers/xml/contact_create.rb b/lib/deserializers/xml/contact_create.rb index 5bd9c768a..5dfa32ef7 100644 --- a/lib/deserializers/xml/contact_create.rb +++ b/lib/deserializers/xml/contact_create.rb @@ -4,24 +4,7 @@ require 'deserializers/xml/contact' module Deserializers module Xml - class ContactCreate - attr_reader :frame - - def initialize(frame) - @frame = frame - end - - def contact - @contact ||= ::Deserializers::Xml::Contact.new(frame).call - end - - def legal_document - @legal_document ||= ::Deserializers::Xml::LegalDocument.new(frame).call - end - - def ident - @ident ||= ::Deserializers::Xml::Ident.new(frame).call - end + class ContactCreate < ContactUpdate end end end From c31dc15207326a5a47490ef7afea60d7daf7d980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 13 Oct 2020 16:37:47 +0300 Subject: [PATCH 042/202] REPP: Move nameservers endpoint to Rails API format --- app/api/repp/nameservers_v1.rb | 45 ------------------- .../v1/registrar/nameservers_controller.rb | 40 +++++++++++++++++ config/routes.rb | 7 +++ 3 files changed, 47 insertions(+), 45 deletions(-) delete mode 100644 app/api/repp/nameservers_v1.rb create mode 100644 app/controllers/repp/v1/registrar/nameservers_controller.rb diff --git a/app/api/repp/nameservers_v1.rb b/app/api/repp/nameservers_v1.rb deleted file mode 100644 index 04d7d4f6a..000000000 --- a/app/api/repp/nameservers_v1.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Repp - class NameserversV1 < Grape::API - version 'v1', using: :path - - resource 'registrar/nameservers' do - put '/' do - params do - requires :data, type: Hash, allow_blank: false do - requires :type, type: String, allow_blank: false - requires :id, type: String, allow_blank: false - requires :attributes, type: Hash, allow_blank: false do - requires :hostname, type: String, allow_blank: false - requires :ipv4, type: Array - requires :ipv6, type: Array - end - end - end - - hostname = params[:data][:id] - - unless current_user.registrar.nameservers.exists?(hostname: hostname) - error!({ errors: [{ title: "Hostname #{hostname} does not exist" }] }, 404) - end - - new_attributes = { - hostname: params[:data][:attributes][:hostname], - ipv4: params[:data][:attributes][:ipv4], - ipv6: params[:data][:attributes][:ipv6], - } - - begin - affected_domains = current_user.registrar.replace_nameservers(hostname, new_attributes) - rescue ActiveRecord::RecordInvalid => e - error!({ errors: e.record.errors.full_messages.map { |error| { title: error } } }, 400) - end - - status 200 - @response = { data: { type: 'nameserver', - id: params[:data][:attributes][:hostname], - attributes: params[:data][:attributes] }, - affected_domains: affected_domains } - end - end - end -end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb new file mode 100644 index 000000000..877c9bf2e --- /dev/null +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -0,0 +1,40 @@ +module Repp + module V1 + module Registrar + class NameserversController < BaseController + before_action :verify_nameserver_existance, only: %i[update] + + def update + domains = current_user.registrar + .replace_nameservers(hostname, hostname_params[:data][:attributes]) + + render_success(data: data_format_for_success(domains)) + rescue ActiveRecord::RecordInvalid => e + handle_errors(e.record) + end + + private + + def data_format_for_success(affected_domains) + { type: 'nameserver', id: params[:data][:attributes][:hostname], + attributes: params[:data][:attributes], affected_domains: affected_domains } + end + + def hostname_params + params.require(:data).require(%i[type id]) + params.require(:data).require(:attributes).require(%i[hostname ipv4 ipv6]) + + params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) + end + + def hostname + hostname_params[:data][:id] + end + + def verify_nameserver_existance + current_user.registrar.nameservers.find_by!(hostname: hostname) + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 634b40e31..1003a75ce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,13 @@ Rails.application.routes.draw do end resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] + namespace :registrar do + resources :nameservers do + collection do + put '/', to: 'nameservers#update' + end + end + end end end From 6c655e11790f2089504ece347e00096e3fea1bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 10:58:51 +0300 Subject: [PATCH 043/202] REPP: Move Domains#Contacts to rails API format --- app/api/repp/domain_contacts_v1.rb | 47 ------------------- .../repp/v1/domains/contacts_controller.rb | 39 +++++++++++++++ config/routes.rb | 7 +++ 3 files changed, 46 insertions(+), 47 deletions(-) delete mode 100644 app/api/repp/domain_contacts_v1.rb create mode 100644 app/controllers/repp/v1/domains/contacts_controller.rb diff --git a/app/api/repp/domain_contacts_v1.rb b/app/api/repp/domain_contacts_v1.rb deleted file mode 100644 index 7f3e323ac..000000000 --- a/app/api/repp/domain_contacts_v1.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Repp - class DomainContactsV1 < Grape::API - version 'v1', using: :path - - resource :domains do - resource :contacts do - patch '/' do - current_contact = current_user.registrar.contacts - .find_by(code: params[:current_contact_id]) - new_contact = current_user.registrar.contacts.find_by(code: params[:new_contact_id]) - - unless current_contact - error!({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: "No such contact: #{params[:current_contact_id]}"} }, - :bad_request) - end - - unless new_contact - error!({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: "No such contact: #{params[:new_contact_id]}" } }, - :bad_request) - end - - if new_contact.invalid? - error!({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'New contact must be valid' } }, - :bad_request) - end - - if current_contact == new_contact - error!({ error: { type: 'invalid_request_error', - message: 'New contact ID must be different from current' \ - ' contact ID' } }, - :bad_request) - end - - affected_domains, skipped_domains = TechDomainContact - .replace(current_contact, new_contact) - @response = { affected_domains: affected_domains, skipped_domains: skipped_domains } - end - end - end - end -end diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb new file mode 100644 index 000000000..226981b60 --- /dev/null +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -0,0 +1,39 @@ +module Repp + module V1 + module Domains + class ContactsController < BaseController + before_action :set_current_contact, only: [:update] + before_action :set_new_contact, only: [:update] + + def set_current_contact + @current_contact = current_user.registrar.contacts.find_by!( + code: params[:current_contact_id] + ) + end + + def set_new_contact + @new_contact = current_user.registrar.contacts.find_by!(code: params[:new_contact_id]) + end + + def update + @epp_errors << { code: '2304', msg: 'New contact must be valid' } if @new_contact.invalid? + + if @new_contact == @current_contact + @epp_errors << { code: '2304', msg: 'New contact must be different from current' } + end + + affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) + data = { affected_domains: affected, skipped_domains: skipped } + render_success(data: data) + end + + private + + def contact_params + params.require(%i[current_contact_id new_contact_id]) + params.permit(:current_contact_id, :new_contact_id) + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 1003a75ce..f1557a659 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,13 @@ Rails.application.routes.draw do end end end + namespace :domains do + resources :contacts do + collection do + patch '/', to: 'contacts#update' + end + end + end end end From d1ab61f424daa2f0da4595c1b8a1fc6376ca89cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 12:42:15 +0300 Subject: [PATCH 044/202] REPP: Move domain transfer info to rails API format --- app/api/repp/domain_v1.rb | 50 ---------------- app/controllers/repp/v1/base_controller.rb | 4 +- app/controllers/repp/v1/domains_controller.rb | 60 +++++++++++++++++++ config/routes.rb | 6 ++ 4 files changed, 68 insertions(+), 52 deletions(-) delete mode 100644 app/api/repp/domain_v1.rb create mode 100644 app/controllers/repp/v1/domains_controller.rb diff --git a/app/api/repp/domain_v1.rb b/app/api/repp/domain_v1.rb deleted file mode 100644 index cf45bfc6f..000000000 --- a/app/api/repp/domain_v1.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Repp - class DomainV1 < Grape::API - version 'v1', using: :path - - resource :domains do - desc 'Return list of domains' - params do - optional :limit, type: Integer, values: (1..200).to_a, desc: 'How many domains to show' - optional :offset, type: Integer, desc: 'Domain number to start at' - optional :details, type: String, values: %w(true false), desc: 'Whether to include details' - end - - get '/' do - limit = params[:limit] || 200 - offset = params[:offset] || 0 - - if params[:details] == 'true' - domains = current_user.registrar.domains.limit(limit).offset(offset) - else - domains = current_user.registrar.domains.limit(limit).offset(offset).pluck(:name) - end - - @response = { - domains: domains, - total_number_of_records: current_user.registrar.domains.count - } - end - - # example: curl -u registrar1:password localhost:3000/repp/v1/domains/1/transfer_info -H "Auth-Code: authinfopw1" - get '/:id/transfer_info', requirements: { id: /.*/ } do - ident = params[:id] - domain = ident.match?(/\A[0-9]+\z/) ? Domain.find_by(id: ident) : Domain.find_by_idn(ident) - - error! I18n.t('errors.messages.epp_domain_not_found'), 404 unless domain - error! I18n.t('errors.messages.epp_authorization_error'), 401 unless domain.transfer_code.eql? request.headers['Auth-Code'] - - contact_repp_json = proc{|contact| - contact.as_json.slice("code", "name", "ident", "ident_type", "ident_country_code", "phone", "email", "street", "city", "zip","country_code", "statuses") - } - - @response = { - domain: domain.name, - registrant: contact_repp_json.call(domain.registrant), - admin_contacts: domain.admin_contacts.map{|e| contact_repp_json.call(e)}, - tech_contacts: domain.tech_contacts.map{|e| contact_repp_json.call(e)} - } - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 3cd636eb2..882c26fb0 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -50,10 +50,10 @@ module Repp end end - def render_epp_error + def render_epp_error(status = :bad_request) render( json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, - status: :bad_request + status: status ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb new file mode 100644 index 000000000..e2188730d --- /dev/null +++ b/app/controllers/repp/v1/domains_controller.rb @@ -0,0 +1,60 @@ +module Repp + module V1 + class DomainsController < BaseController + before_action :set_authorized_domain, only: [:transfer_info] + + def index + records = current_user.registrar.domains + domains = records.limit(limit).offset(offset) + domains = domains.pluck(:name) unless params[:details] == 'true' + + render_success(data: { domains: domains, total_number_of_records: records.count }) + end + + def transfer_info + contact_fields = %i[code name ident ident_type ident_country_code phone email street city + zip country_code statuses] + + data = { + domain: @domain.name, + registrant: @domain.registrant.as_json(only: contact_fields), + admin_contacts: @domain.admin_contacts.map { |c| c.as_json(only: contact_fields) }, + tech_contacts: @domain.tech_contacts.map { |c| c.as_json(only: contact_fields) }, + } + + render_success(data: data) + end + + private + + def transfer_info_params + params.require(:id) + params.permit(:id) + end + + def set_authorized_domain + @epp_errors ||= [] + h = {} + h[transfer_info_params[:id].match?(/\A[0-9]+\z/) ? :id : :name] = transfer_info_params[:id] + @domain = Domain.find_by!(h) + + return if @domain.transfer_code.eql?(request.headers['Auth-Code']) + + @epp_errors << { code: '401', msg: I18n.t('errors.messages.epp_authorization_error') } + handle_errors + end + + def limit + params[:limit] || 200 + end + + def offset + params[:offset] || 0 + end + + def index_params + params.permit(:limit, :offset, :details) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index f1557a659..39cc4a0c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,12 @@ Rails.application.routes.draw do end end end + resources :domains do + collection do + get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } + end + end + namespace :domains do resources :contacts do collection do From ee199f9318cf3b22629c24fe127289ee44257937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 15:46:57 +0300 Subject: [PATCH 045/202] REPP: Move domain transfer to rails API format --- app/api/repp/domain_transfers_v1.rb | 48 ------------------- app/controllers/repp/v1/domains_controller.rb | 41 ++++++++++++++++ config/routes.rb | 1 + 3 files changed, 42 insertions(+), 48 deletions(-) delete mode 100644 app/api/repp/domain_transfers_v1.rb diff --git a/app/api/repp/domain_transfers_v1.rb b/app/api/repp/domain_transfers_v1.rb deleted file mode 100644 index c6a48df6d..000000000 --- a/app/api/repp/domain_transfers_v1.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Repp - class DomainTransfersV1 < Grape::API - version 'v1', using: :path - - resource :domain_transfers do - post '/' do - params do - requires :data, type: Hash do - requires :domainTransfers, type: Array do - requires :domainName, type: String, allow_blank: false - requires :transferCode, type: String, allow_blank: false - end - end - end - - new_registrar = current_user.registrar - domain_transfers = params['data']['domainTransfers'] - successful_domain_transfers = [] - errors = [] - - domain_transfers.each do |domain_transfer| - domain_name = domain_transfer['domainName'] - transfer_code = domain_transfer['transferCode'] - domain = Domain.find_by(name: domain_name) - - if domain - if domain.transfer_code == transfer_code - DomainTransfer.request(domain, new_registrar) - successful_domain_transfers << { type: 'domain_transfer', attributes: { domain_name: domain.name } } - else - errors << { title: "#{domain_name} transfer code is wrong" } - end - else - errors << { title: "#{domain_name} does not exist" } - end - end - - if errors.none? - status 200 - @response = { data: successful_domain_transfers } - else - status 400 - @response = { errors: errors } - end - end - end - end -end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index e2188730d..4f3d8afe3 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -25,8 +25,49 @@ module Repp render_success(data: data) end + def transfer + @errors ||= [] + successful = [] + + params[:data][:domain_transfers].each do |transfer| + domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) + next unless domain + + DomainTransfer.request(domain, current_user.registrar) + successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } + end + + render_success(data: { errors: @errors }) and return if @errors.any? + + render_success(data: successful) + end + + def transferable_domain(domain_name, transfer_code) + domain = Domain.find_by(name: domain_name) + valid_transfer_code = domain.transfer_code == transfer_code + add_error("#{domain_name} does not exist") and return unless domain + add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code + + domain + end + + def add_error(msg) + @errors ||= [] + @errors << { title: msg } + end + private + def transfer_params + params.require(:data).require(:domain_transfers).each do |t| + t.require(:domain_name) + t.permit(:domain_name) + t.require(:transfer_code) + t.permit(:transfer_code) + end + params.require(:data).permit(domain_transfers: %i[domain_name transfer_code]) + end + def transfer_info_params params.require(:id) params.permit(:id) diff --git a/config/routes.rb b/config/routes.rb index 39cc4a0c3..3827d8ef9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -62,6 +62,7 @@ Rails.application.routes.draw do resources :domains do collection do get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } + post 'transfer', to: 'domains#transfer' end end From 9b8d74fe30e7e46a8710340d207ca4455324020d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 15:53:54 +0300 Subject: [PATCH 046/202] Fic some CC issues --- app/controllers/repp/v1/domains_controller.rb | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 4f3d8afe3..32ae61687 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -27,26 +27,34 @@ module Repp def transfer @errors ||= [] - successful = [] + @successful = [] params[:data][:domain_transfers].each do |transfer| - domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - next unless domain - - DomainTransfer.request(domain, current_user.registrar) - successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } + initiate_transfer(transfer) end - render_success(data: { errors: @errors }) and return if @errors.any? + if @errors.any + render_success(data: { errors: @errors }) + else + render_success(data: successful) + end + end - render_success(data: successful) + def initiate_transfer(transfer) + domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) + next unless domain + + DomainTransfer.request(domain, current_user.registrar) + @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } end def transferable_domain(domain_name, transfer_code) domain = Domain.find_by(name: domain_name) valid_transfer_code = domain.transfer_code == transfer_code + # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code + # rubocop:enable Style/AndOr domain end From 6d96a09b5cfdfb3df982ff40f153fe6a686a5a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:04:27 +0300 Subject: [PATCH 047/202] Save REPP log after every request --- app/controllers/repp/v1/base_controller.rb | 11 ++++++++++- app/controllers/repp/v1/domains_controller.rb | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 882c26fb0..7aac47d80 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,13 +4,22 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction - attr_reader :current_user rescue_from ActionController::ParameterMissing do |exception| render json: { code: 2003, message: exception }, status: :bad_request end + after_action do + ApiLog::ReppLog.create( + { request_path: request.path, request_method: request.request_method, + request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), + response: @response.to_json, response_code: status, ip: request.ip, + api_user_name: current_user.try(:username), + api_user_registrar: current_user.try(:registrar).try(:to_s) } + ) + end + private def render_success(code: nil, message: nil, data: nil) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 32ae61687..e130d7a68 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,7 +33,7 @@ module Repp initiate_transfer(transfer) end - if @errors.any + if @errors.any? render_success(data: { errors: @errors }) else render_success(data: successful) @@ -42,7 +42,7 @@ module Repp def initiate_transfer(transfer) domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - next unless domain + return unless domain DomainTransfer.request(domain, current_user.registrar) @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } From 951a3f247b8f3251c2c0e4de7226543651985a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:43:45 +0300 Subject: [PATCH 048/202] Fix some CC issues --- app/controllers/repp/v1/base_controller.rb | 10 +++++----- app/controllers/repp/v1/domains_controller.rb | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 7aac47d80..38f3a486a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -12,11 +12,11 @@ module Repp after_action do ApiLog::ReppLog.create( - { request_path: request.path, request_method: request.request_method, - request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), - response: @response.to_json, response_code: status, ip: request.ip, - api_user_name: current_user.try(:username), - api_user_registrar: current_user.try(:registrar).try(:to_s) } + request_path: request.path, request_method: request.request_method, + request_params: request.params.except('route_info').to_json, uuid: request.try(:uuid), + response: @response.to_json, response_code: status, ip: request.ip, + api_user_name: current_user.try(:username), + api_user_registrar: current_user.try(:registrar).try(:to_s) ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index e130d7a68..0d1470e6a 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -50,9 +50,9 @@ module Repp def transferable_domain(domain_name, transfer_code) domain = Domain.find_by(name: domain_name) - valid_transfer_code = domain.transfer_code == transfer_code # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain + valid_transfer_code = domain.transfer_code.eql?(transfer_code) add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code # rubocop:enable Style/AndOr From b8311ac5f6b253fd0691e155d88e330619e5d45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 16:48:46 +0300 Subject: [PATCH 049/202] Remove Grape from codebase --- Gemfile | 2 -- Gemfile.lock | 34 --------------------- app/api/repp/api.rb | 73 --------------------------------------------- 3 files changed, 109 deletions(-) delete mode 100644 app/api/repp/api.rb diff --git a/Gemfile b/Gemfile index 6ba54e871..34bc9b7ac 100644 --- a/Gemfile +++ b/Gemfile @@ -35,8 +35,6 @@ gem 'select2-rails', '3.5.9.3' # for autocomplete gem 'cancancan' gem 'devise', '~> 4.7' -gem 'grape' - # registry specfic gem 'data_migrate', '~> 6.1' gem 'isikukood' # for EE-id validation diff --git a/Gemfile.lock b/Gemfile.lock index 1a45ed826..ff6dcd3a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -196,28 +196,6 @@ GEM docile (1.3.2) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dry-configurable (0.11.6) - concurrent-ruby (~> 1.0) - dry-core (~> 0.4, >= 0.4.7) - dry-equalizer (~> 0.2) - dry-container (0.7.2) - concurrent-ruby (~> 1.0) - dry-configurable (~> 0.1, >= 0.1.3) - dry-core (0.4.9) - concurrent-ruby (~> 1.0) - dry-equalizer (0.3.0) - dry-inflector (0.2.0) - dry-logic (1.0.7) - concurrent-ruby (~> 1.0) - dry-core (~> 0.2) - dry-equalizer (~> 0.2) - dry-types (1.4.0) - concurrent-ruby (~> 1.0) - dry-container (~> 0.3) - dry-core (~> 0.4, >= 0.4.4) - dry-equalizer (~> 0.3) - dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.0, >= 1.0.2) erubi (1.9.0) erubis (2.7.0) execjs (2.7.0) @@ -226,13 +204,6 @@ GEM thor (~> 0.14) globalid (0.4.2) activesupport (>= 4.2.0) - grape (1.4.0) - activesupport - builder - dry-types (>= 1.1) - mustermann-grape (~> 1.0.0) - rack (>= 1.3.0) - rack-accept gyoku (1.3.1) builder (>= 2.1.2) haml (5.1.2) @@ -312,8 +283,6 @@ GEM multi_json (1.15.0) mustermann (1.1.1) ruby2_keywords (~> 0.0.1) - mustermann-grape (1.0.1) - mustermann (>= 1.0.0) netrc (0.11.0) nio4r (2.5.4) nokogiri (1.10.10) @@ -357,8 +326,6 @@ GEM que (~> 0.8) sinatra rack (2.2.3) - rack-accept (0.4.5) - rack (>= 0.4) rack-oauth2 (1.16.0) activesupport attr_required @@ -542,7 +509,6 @@ DEPENDENCIES epp! epp-xml (= 1.1.0)! figaro (= 1.1.1) - grape haml (~> 5.0) isikukood iso8601 (= 0.12.1) diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb deleted file mode 100644 index f1908f81e..000000000 --- a/app/api/repp/api.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Repp - class API < Grape::API - format :json - prefix :repp - - rescue_from Grape::Exceptions::ValidationErrors do |e| - messages = e.full_messages - errors = [] - messages.each { |m| errors << { code: 2003, message: m } } - - error!({ errors: errors }, 400) - end - - http_basic do |username, password| - @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) - if @current_user - true - else - error! I18n.t('api_user_not_found'), 401 - end - end - - before do - webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip) - unless webclient_request - error! I18n.t('api.authorization.ip_not_allowed', ip: request.ip), 401 unless @current_user.registrar.api_ip_white?(request.ip) - end - - if @current_user.cannot?(:view, :repp) - error! I18n.t('no_permission'), 401 unless @current_user.registrar.api_ip_white?(request.ip) - end - - next if Rails.env.test? || Rails.env.development? - message = 'Certificate mismatch! Cert common name should be:' - request_name = env['HTTP_SSL_CLIENT_S_DN_CN'] - - if webclient_request - webclient_cert_name = ENV['webclient_cert_common_name'] || 'webclient' - error! "Webclient #{message} #{webclient_cert_name}", 401 if webclient_cert_name != request_name - else - unless @current_user.pki_ok?(request.env['HTTP_SSL_CLIENT_CERT'], - request.env['HTTP_SSL_CLIENT_S_DN_CN']) - error! "#{message} #{@current_user.username}", 401 - end - end - end - - helpers do - attr_reader :current_user - end - - after do - ApiLog::ReppLog.create({ - request_path: request.path, - request_method: request.request_method, - request_params: request.params.except('route_info').to_json, - response: @response.to_json, - response_code: status, - api_user_name: current_user.try(:username), - api_user_registrar: current_user.try(:registrar).try(:to_s), - ip: request.ip, - uuid: request.try(:uuid) - }) - end - - mount Repp::DomainV1 - mount Repp::ContactV1 - mount Repp::AccountV1 - mount Repp::DomainTransfersV1 - mount Repp::NameserversV1 - mount Repp::DomainContactsV1 - end -end From 27774cc275089feec5ea8c62a39a2834015e4235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 14 Oct 2020 17:09:47 +0300 Subject: [PATCH 050/202] REPP: Fix tests --- test/integration/api/domain_transfers_test.rb | 20 +++++++++---------- test/integration/api/nameservers/put_test.rb | 7 +++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index aabaeb728..25d245265 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -13,7 +13,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest end def test_returns_domain_transfers - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 assert_equal ({ data: [{ @@ -27,19 +27,19 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_creates_new_domain_transfer assert_difference -> { @domain.transfers.size } do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_approves_automatically_if_auto_approval_is_enabled - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert @domain.transfers.last.approved? end def test_assigns_new_registrar - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } @domain.reload assert_equal @new_registrar, @domain.registrar @@ -48,7 +48,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_regenerates_transfer_code @old_transfer_code = @domain.transfer_code - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } @domain.reload refute_equal @domain.transfer_code, @old_transfer_code @@ -58,26 +58,26 @@ class APIDomainTransfersTest < ApplicationIntegrationTest @old_registrar = @domain.registrar assert_difference -> { @old_registrar.notifications.count } do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_duplicates_registrant_admin_and_tech_contacts assert_difference -> { @new_registrar.contacts.size }, 3 do - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } end end def test_reuses_identical_contact - post '/repp/v1/domain_transfers', params: request_params, as: :json, + post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_equal 1, @new_registrar.contacts.where(name: 'William').size end def test_fails_if_domain_does_not_exist - post '/repp/v1/domain_transfers', + post '/repp/v1/domains/transfer', params: { data: { domainTransfers: [{ domainName: 'non-existent.test', transferCode: 'any' }] } }, as: :json, @@ -88,7 +88,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest end def test_fails_if_transfer_code_is_wrong - post '/repp/v1/domain_transfers', + post '/repp/v1/domains/transfer', params: { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: 'wrong' }] } }, as: :json, diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index 853a20549..d564c5cda 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -60,7 +60,9 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ data: { type: 'nameserver', + assert_equal ({ code: '1000', + message: 'Command completed successfully', + data: { type: 'nameserver', id: 'ns55.bestnames.test', attributes: { hostname: 'ns55.bestnames.test', ipv4: ['192.0.2.55'], @@ -96,7 +98,8 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ errors: [{ title: 'Hostname is missing' }] }), + assert_equal ({ code: '2003', + message: 'param is missing or the value is empty: hostname' }), JSON.parse(response.body, symbolize_names: true) end From a6f7af0f03fa655a62462da13bbb77381c215374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 15 Oct 2020 10:23:50 +0300 Subject: [PATCH 051/202] Fix tests --- .../registrar/domain_transfers_controller.rb | 6 ++-- app/controllers/repp/v1/base_controller.rb | 2 ++ app/controllers/repp/v1/domains_controller.rb | 2 +- .../v1/registrar/nameservers_controller.rb | 2 +- config/routes.rb | 2 +- test/integration/api/domain_transfers_test.rb | 31 ++++++++++++------- test/integration/api/nameservers/put_test.rb | 10 +++--- .../bulk_change/bulk_transfer_test.rb | 6 ++-- 8 files changed, 35 insertions(+), 26 deletions(-) diff --git a/app/controllers/registrar/domain_transfers_controller.rb b/app/controllers/registrar/domain_transfers_controller.rb index acacc3ef4..ca08c73cf 100644 --- a/app/controllers/registrar/domain_transfers_controller.rb +++ b/app/controllers/registrar/domain_transfers_controller.rb @@ -15,12 +15,12 @@ class Registrar csv.each do |row| domain_name = row['Domain'] transfer_code = row['Transfer code'] - domain_transfers << { 'domainName' => domain_name, 'transferCode' => transfer_code } + domain_transfers << { 'domain_name' => domain_name, 'transfer_code' => transfer_code } end - uri = URI.parse("#{ENV['repp_url']}domain_transfers") + uri = URI.parse("#{ENV['repp_url']}domains/transfer") request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') - request.body = { data: { domainTransfers: domain_transfers } }.to_json + request.body = { data: { domain_transfers: domain_transfers } }.to_json request.basic_auth(current_registrar_user.username, current_registrar_user.plain_text_password) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 38f3a486a..f8a465e77 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -82,6 +82,8 @@ module Repp return if @current_user + render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + rescue NoMethodError render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 0d1470e6a..237ed5fe4 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -36,7 +36,7 @@ module Repp if @errors.any? render_success(data: { errors: @errors }) else - render_success(data: successful) + render_success(data: @successful) end end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 877c9bf2e..7e00cf2ac 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -22,7 +22,7 @@ module Repp def hostname_params params.require(:data).require(%i[type id]) - params.require(:data).require(:attributes).require(%i[hostname ipv4 ipv6]) + params.require(:data).require(:attributes) params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) end diff --git a/config/routes.rb b/config/routes.rb index 3827d8ef9..282a44933 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ Rails.application.routes.draw do namespace :domains do resources :contacts do collection do - patch '/', to: 'contacts#update' + patch '/', to: 'domains/contacts#update' end end end diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index 25d245265..ce6235268 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -16,13 +16,20 @@ class APIDomainTransfersTest < ApplicationIntegrationTest post '/repp/v1/domains/transfer', params: request_params, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ data: [{ - type: 'domain_transfer', - attributes: { - domain_name: 'shop.test' - }, - }] }), - JSON.parse(response.body, symbolize_names: true) + + expected_body = { + code: 1000, + message: 'Command completed successfully', + data: [ + { + type: 'domain_transfer', + attributes: { domain_name: 'shop.test' }, + } + ] + } + + real_body = JSON.parse(response.body, symbolize_names: true) + assert_equal(expected_body, real_body) end def test_creates_new_domain_transfer @@ -78,8 +85,8 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_fails_if_domain_does_not_exist post '/repp/v1/domains/transfer', - params: { data: { domainTransfers: [{ domainName: 'non-existent.test', - transferCode: 'any' }] } }, + params: { data: { domain_transfers: [{ domain_name: 'non-existent.test', + transfer_code: 'any' }] } }, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 @@ -89,8 +96,8 @@ class APIDomainTransfersTest < ApplicationIntegrationTest def test_fails_if_transfer_code_is_wrong post '/repp/v1/domains/transfer', - params: { data: { domainTransfers: [{ domainName: 'shop.test', - transferCode: 'wrong' }] } }, + params: { data: { domain_transfers: [{ domain_name: 'shop.test', + transfer_code: 'wrong' }] } }, as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 @@ -102,7 +109,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest private def request_params - { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: '65078d5' }] } } + { data: { domain_transfers: [{ domain_name: 'shop.test', transfer_code: '65078d5' }] } } end def http_auth_key diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index d564c5cda..3ab4f4dd4 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -60,14 +60,14 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 200 - assert_equal ({ code: '1000', + assert_equal ({ code: 1000, message: 'Command completed successfully', data: { type: 'nameserver', id: 'ns55.bestnames.test', attributes: { hostname: 'ns55.bestnames.test', ipv4: ['192.0.2.55'], - ipv6: ['2001:db8::55'] } }, - affected_domains: ["airport.test", "shop.test"] }), + ipv6: ['2001:db8::55'] }, + affected_domains: ["airport.test", "shop.test"] }}), JSON.parse(response.body, symbolize_names: true) end @@ -87,7 +87,7 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 404 - assert_equal ({ errors: [{ title: 'Hostname non-existent.test does not exist' }] }), + assert_equal ({code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -98,7 +98,7 @@ class APINameserversPutTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ code: '2003', + assert_equal ({ code: 2003, message: 'param is missing or the value is empty: hostname' }), JSON.parse(response.body, symbolize_names: true) end diff --git a/test/system/registrar_area/bulk_change/bulk_transfer_test.rb b/test/system/registrar_area/bulk_change/bulk_transfer_test.rb index 69b755499..a531ff4dc 100644 --- a/test/system/registrar_area/bulk_change/bulk_transfer_test.rb +++ b/test/system/registrar_area/bulk_change/bulk_transfer_test.rb @@ -6,9 +6,9 @@ class RegistrarAreaBulkTransferTest < ApplicationSystemTestCase end def test_transfer_multiple_domains_in_bulk - request_body = { data: { domainTransfers: [{ domainName: 'shop.test', transferCode: '65078d5' }] } } + request_body = { data: { domain_transfers: [{ domain_name: 'shop.test', transfer_code: '65078d5' }] } } headers = { 'Content-type' => Mime[:json] } - request_stub = stub_request(:post, /domain_transfers/).with(body: request_body, + request_stub = stub_request(:post, /domains\/transfer/).with(body: request_body, headers: headers, basic_auth: ['test_goodnames', 'testtest']) .to_return(body: { data: [{ @@ -29,7 +29,7 @@ class RegistrarAreaBulkTransferTest < ApplicationSystemTestCase def test_fail_gracefully body = { errors: [{ title: 'epic fail' }] }.to_json headers = { 'Content-type' => Mime[:json] } - stub_request(:post, /domain_transfers/).to_return(status: 400, body: body, headers: headers) + stub_request(:post, /domains\/transfer/).to_return(status: 400, body: body, headers: headers) visit registrar_domains_url click_link 'Bulk change' From f2a1ee101b5055e515ff7dbd5fe32b5f0a72dc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 16 Oct 2020 12:12:17 +0300 Subject: [PATCH 052/202] Fix tests --- config/routes.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 4eb09544f..47c4d0943 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,14 +63,7 @@ Rails.application.routes.draw do collection do get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } post 'transfer', to: 'domains#transfer' - end - end - - namespace :domains do - resources :contacts do - collection do - patch '/', to: 'domains/contacts#update' - end + patch 'contacts', to: 'domains/contacts#update' end end end From 220e0d79933b244f5f4fe2e017a581df89d5765d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 16 Oct 2020 12:33:50 +0300 Subject: [PATCH 053/202] Fix tests --- app/controllers/repp/v1/base_controller.rb | 7 +++-- .../repp/v1/domains/contacts_controller.rb | 7 +++-- app/controllers/repp/v1/domains_controller.rb | 9 +++--- .../v1/registrar/nameservers_controller.rb | 2 +- test/integration/api/domain_contacts_test.rb | 31 +++++++------------ test/integration/api/domain_transfers_test.rb | 4 +-- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index f8a465e77..b3aa786d1 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -59,9 +59,12 @@ module Repp end end - def render_epp_error(status = :bad_request) + def render_epp_error(status = :bad_request, data = {}) + @epp_errors ||= [] + @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} + render( - json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg] }, + json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg], data: data }, status: status ) end diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 226981b60..70f64ed28 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -16,12 +16,15 @@ module Repp end def update - @epp_errors << { code: '2304', msg: 'New contact must be valid' } if @new_contact.invalid? + @epp_errors ||= [] + @epp_errors << { code: 2304, msg: 'New contact must be valid' } if @new_contact.invalid? if @new_contact == @current_contact - @epp_errors << { code: '2304', msg: 'New contact must be different from current' } + @epp_errors << { code: 2304, msg: 'New contact must be different from current' } end + return handle_errors if @epp_errors.any? + affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) data = { affected_domains: affected, skipped_domains: skipped } render_success(data: data) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 237ed5fe4..163731c69 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -34,7 +34,7 @@ module Repp end if @errors.any? - render_success(data: { errors: @errors }) + render_epp_error(:bad_request, @errors) else render_success(data: @successful) end @@ -52,10 +52,11 @@ module Repp domain = Domain.find_by(name: domain_name) # rubocop:disable Style/AndOr add_error("#{domain_name} does not exist") and return unless domain - valid_transfer_code = domain.transfer_code.eql?(transfer_code) - add_error("#{domain_name} transfer code is wrong") and return unless valid_transfer_code # rubocop:enable Style/AndOr - + unless domain.transfer_code.eql?(transfer_code) + add_error("#{domain_name} transfer code is wrong") + return + end domain end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 7e00cf2ac..1af85b760 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -22,7 +22,7 @@ module Repp def hostname_params params.require(:data).require(%i[type id]) - params.require(:data).require(:attributes) + params.require(:data).require(:attributes).require([:hostname]) params.permit(data: [:type, :id, attributes: [:hostname, ipv4: [], ipv6: []]]) end diff --git a/test/integration/api/domain_contacts_test.rb b/test/integration/api/domain_contacts_test.rb index 5336cc10a..6704739d1 100644 --- a/test/integration/api/domain_contacts_test.rb +++ b/test/integration/api/domain_contacts_test.rb @@ -27,8 +27,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :ok - assert_equal ({ affected_domains: %w[airport.test shop.test], - skipped_domains: [] }), + assert_equal ({ code: 1000, message: 'Command completed successfully', data: { affected_domains: %w[airport.test shop.test], + skipped_domains: [] }}), JSON.parse(response.body, symbolize_names: true) end @@ -42,7 +42,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest assert_response :ok assert_equal %w[airport.test shop.test], JSON.parse(response.body, - symbolize_names: true)[:skipped_domains] + symbolize_names: true)[:data][:skipped_domains] end def test_keep_other_tech_contacts_intact @@ -66,10 +66,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'william-002' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: 'No such contact: jack-001' } }), + assert_response :not_found + assert_equal ({ code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -77,10 +75,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest patch '/repp/v1/domains/contacts', params: { current_contact_id: 'non-existent', new_contact_id: 'john-001' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'current_contact_id', - message: 'No such contact: non-existent' } }), + assert_response :not_found + assert_equal ({ code: 2303, message: 'Object does not exist' }), JSON.parse(response.body, symbolize_names: true) end @@ -88,10 +84,8 @@ class APIDomainContactsTest < ApplicationIntegrationTest patch '/repp/v1/domains/contacts', params: { current_contact_id: 'william-001', new_contact_id: 'non-existent' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'No such contact: non-existent' } }), + assert_response :not_found + assert_equal ({code: 2303, message: 'Object does not exist'}), JSON.parse(response.body, symbolize_names: true) end @@ -100,9 +94,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'invalid' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - param: 'new_contact_id', - message: 'New contact must be valid' } }), + assert_equal ({ code: 2304, message: 'New contact must be valid', data: {} }), JSON.parse(response.body, symbolize_names: true) end @@ -111,8 +103,7 @@ class APIDomainContactsTest < ApplicationIntegrationTest new_contact_id: 'william-001' }, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response :bad_request - assert_equal ({ error: { type: 'invalid_request_error', - message: 'New contact ID must be different from current contact ID' } }), + assert_equal ({ code: 2304, message: 'New contact must be different from current', data: {} }), JSON.parse(response.body, symbolize_names: true) end diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index ce6235268..ecb69d262 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -90,7 +90,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest as: :json, headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 - assert_equal ({ errors: [{ title: 'non-existent.test does not exist' }] }), + assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'non-existent.test does not exist' }] }), JSON.parse(response.body, symbolize_names: true) end @@ -102,7 +102,7 @@ class APIDomainTransfersTest < ApplicationIntegrationTest headers: { 'HTTP_AUTHORIZATION' => http_auth_key } assert_response 400 refute_equal @new_registrar, @domain.registrar - assert_equal ({ errors: [{ title: 'shop.test transfer code is wrong' }] }), + assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'shop.test transfer code is wrong' }] }), JSON.parse(response.body, symbolize_names: true) end From 87dff41a4e1177a271517421a3a491f3255f86f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 16:02:18 +0300 Subject: [PATCH 054/202] REPP: Authentication test --- app/controllers/repp/v1/base_controller.rb | 12 ++++++--- .../repp/{ => v1}/auctions_test.rb | 0 test/integration/repp/v1/base_test.rb | 26 +++++++++++++++++++ test/integration/repp/v1/contacts_test.rb | 24 +++++++++++++++++ .../repp/{ => v1}/retained_domains_test.rb | 0 5 files changed, 58 insertions(+), 4 deletions(-) rename test/integration/repp/{ => v1}/auctions_test.rb (100%) create mode 100644 test/integration/repp/v1/base_test.rb create mode 100644 test/integration/repp/v1/contacts_test.rb rename test/integration/repp/{ => v1}/retained_domains_test.rb (100%) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index b3aa786d1..c46c837f6 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -76,7 +76,8 @@ module Repp def basic_token pattern = /^Basic / header = request.headers['Authorization'] - header.gsub(pattern, '') if header&.match(pattern) + header = header.gsub(pattern, '') if header&.match(pattern) + header.strip end def authenticate_user @@ -85,9 +86,12 @@ module Repp return if @current_user - render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) - rescue NoMethodError - render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + raise(ArgumentError) + rescue NoMethodError, ArgumentError + render( + json: { code: 2202, message: 'Invalid authorization information' }, + status: :unauthorized + ) end def check_ip_restriction diff --git a/test/integration/repp/auctions_test.rb b/test/integration/repp/v1/auctions_test.rb similarity index 100% rename from test/integration/repp/auctions_test.rb rename to test/integration/repp/v1/auctions_test.rb diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb new file mode 100644 index 000000000..931ad094c --- /dev/null +++ b/test/integration/repp/v1/base_test.rb @@ -0,0 +1,26 @@ +require 'test_helper' + +class ReppV1BaseTest < ActionDispatch::IntegrationTest + def setup + @registrant = users(:api_bestnames) + token = Base64.encode64("#{@registrant.username}:#{@registrant.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_unauthorized_user_has_no_access + get repp_v1_contacts_path + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] + end + + def test_authenticates_valid_user + get repp_v1_contacts_path, headers: @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + end +end diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts_test.rb new file mode 100644 index 000000000..92c254dc3 --- /dev/null +++ b/test/integration/repp/v1/contacts_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class ReppV1ContactsTest < ActionDispatch::IntegrationTest + def setup + @auction = auctions(:one) + @auction.update!(uuid: '1b3ee442-e8fe-4922-9492-8fcb9dccc69c', + domain: 'auction.test', + status: Auction.statuses[:started]) + end + + def test_get_index + get repp_v1_contacts_path + response_json = JSON.parse(response.body, symbolize_names: true) + + puts response_json + + assert response_json[:count] == 1 + + expected_response = [{ domain_name: @auction.domain, + punycode_domain_name: @auction.domain }] + + assert_equal expected_response, response_json[:auctions] + end +end diff --git a/test/integration/repp/retained_domains_test.rb b/test/integration/repp/v1/retained_domains_test.rb similarity index 100% rename from test/integration/repp/retained_domains_test.rb rename to test/integration/repp/v1/retained_domains_test.rb From d1e877502de667fbf66e7305dad37e8455d79a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 16:29:28 +0300 Subject: [PATCH 055/202] REPP: Contact list tests --- test/integration/repp/v1/base_test.rb | 4 +- test/integration/repp/v1/contacts_test.rb | 55 ++++++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb index 931ad094c..63255dd24 100644 --- a/test/integration/repp/v1/base_test.rb +++ b/test/integration/repp/v1/base_test.rb @@ -2,8 +2,8 @@ require 'test_helper' class ReppV1BaseTest < ActionDispatch::IntegrationTest def setup - @registrant = users(:api_bestnames) - token = Base64.encode64("#{@registrant.username}:#{@registrant.plain_text_password}") + @registrar = users(:api_bestnames) + token = Base64.encode64("#{@registrar.username}:#{@registrar.plain_text_password}") token = "Basic #{token}" @auth_headers = { 'Authorization' => token } diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts_test.rb index 92c254dc3..353cb3758 100644 --- a/test/integration/repp/v1/contacts_test.rb +++ b/test/integration/repp/v1/contacts_test.rb @@ -2,23 +2,54 @@ require 'test_helper' class ReppV1ContactsTest < ActionDispatch::IntegrationTest def setup - @auction = auctions(:one) - @auction.update!(uuid: '1b3ee442-e8fe-4922-9492-8fcb9dccc69c', - domain: 'auction.test', - status: Auction.statuses[:started]) + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } end - def test_get_index - get repp_v1_contacts_path - response_json = JSON.parse(response.body, symbolize_names: true) + def test_returns_registrar_contacts + get repp_v1_contacts_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) - puts response_json + assert_response :ok - assert response_json[:count] == 1 + assert_equal @user.registrar.contacts.count, json[:total_number_of_records] + assert_equal @user.registrar.contacts.count, json[:contacts].length - expected_response = [{ domain_name: @auction.domain, - punycode_domain_name: @auction.domain }] + assert json[:contacts][0].is_a? String + end - assert_equal expected_response, response_json[:auctions] + + def test_returns_detailed_registrar_contacts + get repp_v1_contacts_path(details: true), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.contacts.count, json[:total_number_of_records] + assert_equal @user.registrar.contacts.count, json[:contacts].length + + assert json[:contacts][0].is_a? Hash + end + + def test_respects_limit_in_registrar_contact_list + get repp_v1_contacts_path(details: true, limit: 2), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 2, json[:contacts].length + end + + def test_respects_offset_in_registrar_contact_list + offset = 1 + get repp_v1_contacts_path(details: true, offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.contacts.count - offset), json[:contacts].length end end From 7f1637c8d2d23d11e71af6509acac0405e8ec60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 19 Oct 2020 17:14:23 +0300 Subject: [PATCH 056/202] REPP: Contacts show test --- .../repp/v1/contacts_controller.rb | 2 +- .../list_test.rb} | 6 +-- .../integration/repp/v1/contacts/show_test.rb | 45 +++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) rename test/integration/repp/v1/{contacts_test.rb => contacts/list_test.rb} (90%) create mode 100644 test/integration/repp/v1/contacts/show_test.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 6321f2a40..0b52505b0 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -81,7 +81,7 @@ module Repp def find_contact code = params[:id] - @contact = Epp::Contact.find_by!(code: code) + @contact = Epp::Contact.find_by!(code: code, registrar: current_user.registrar) end def contact_params_with_address diff --git a/test/integration/repp/v1/contacts_test.rb b/test/integration/repp/v1/contacts/list_test.rb similarity index 90% rename from test/integration/repp/v1/contacts_test.rb rename to test/integration/repp/v1/contacts/list_test.rb index 353cb3758..31c4baaf9 100644 --- a/test/integration/repp/v1/contacts_test.rb +++ b/test/integration/repp/v1/contacts/list_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class ReppV1ContactsTest < ActionDispatch::IntegrationTest +class ReppV1ContactsListTest < ActionDispatch::IntegrationTest def setup @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") @@ -34,7 +34,7 @@ class ReppV1ContactsTest < ActionDispatch::IntegrationTest assert json[:contacts][0].is_a? Hash end - def test_respects_limit_in_registrar_contact_list + def test_respects_limit get repp_v1_contacts_path(details: true, limit: 2), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) @@ -43,7 +43,7 @@ class ReppV1ContactsTest < ActionDispatch::IntegrationTest assert_equal 2, json[:contacts].length end - def test_respects_offset_in_registrar_contact_list + def test_respects_offset offset = 1 get repp_v1_contacts_path(details: true, offset: offset), headers: @auth_headers json = JSON.parse(response.body, symbolize_names: true) diff --git a/test/integration/repp/v1/contacts/show_test.rb b/test/integration/repp/v1/contacts/show_test.rb new file mode 100644 index 000000000..3fd782cca --- /dev/null +++ b/test/integration/repp/v1/contacts/show_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class ReppV1ContactsShowTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_error_when_not_found + get repp_v1_contact_path(id: 'definitelynotexistant'), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_shows_existing_contact + contact = @user.registrar.contacts.first + + get repp_v1_contact_path(id: contact.code), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal contact.code, json[:data][:code] + end + + def test_can_not_access_out_of_scope_contacts + # Contact of registrar goodnames, we're using bestnames API credentials + contact = contacts(:jack) + + get repp_v1_contact_path(id: contact.code), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end +end From 39d09e5543d2e82f8819e9fc57be66a590a1344a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 10:44:48 +0300 Subject: [PATCH 057/202] REPP: Contact check test --- .../repp/v1/contacts/check_test.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/integration/repp/v1/contacts/check_test.rb diff --git a/test/integration/repp/v1/contacts/check_test.rb b/test/integration/repp/v1/contacts/check_test.rb new file mode 100644 index 000000000..be0d979b1 --- /dev/null +++ b/test/integration/repp/v1/contacts/check_test.rb @@ -0,0 +1,30 @@ +require 'test_helper' + +class ReppV1ContactsCheckTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_code_based_check_returns_true_for_available_contact + get '/repp/v1/contacts/check/nonexistant:code', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 'nonexistant:code', json[:data][:contact][:id] + assert_equal true, json[:data][:contact][:available] + end + + def test_code_based_check_returns_true_for_available_contact + contact = contacts(:jack) + get "/repp/v1/contacts/check/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal contact.code, json[:data][:contact][:id] + assert_equal false, json[:data][:contact][:available] + end +end From 9cfa1c2989b80a5fbf4a552297c4c238d7aea1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 11:26:32 +0300 Subject: [PATCH 058/202] REPP: Contact create test --- app/controllers/repp/v1/base_controller.rb | 2 +- .../repp/v1/contacts/create_test.rb | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/contacts/create_test.rb diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index c46c837f6..68cc8d225 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -64,7 +64,7 @@ module Repp @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} render( - json: { code: @epp_errors[0][:code], message: @epp_errors[0][:msg], data: data }, + json: { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data }, status: status ) end diff --git a/test/integration/repp/v1/contacts/create_test.rb b/test/integration/repp/v1/contacts/create_test.rb new file mode 100644 index 000000000..30e71eee9 --- /dev/null +++ b/test/integration/repp/v1/contacts/create_test.rb @@ -0,0 +1,127 @@ +require 'test_helper' + +class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_creates_new_contact + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_equal(request_body[:contact][:name], contact.name) + assert_equal(request_body[:contact][:phone], contact.phone) + assert_equal(request_body[:contact][:email], contact.email) + assert_equal(request_body[:contact][:ident][:ident_type], contact.ident_type) + assert_equal(request_body[:contact][:ident][:ident_country_code], contact.ident_country_code) + assert_equal(request_body[:contact][:ident][:ident], contact.ident) + end + + def test_removes_postal_info_when_contact_created + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111111", + "email": "donald@trump.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + }, + "addr": { + "city": "Tallinn", + "street": "Wismari 13", + "zip": "12345", + "country_code": "EE" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1100, json[:code] + assert_equal 'Command completed successfully; Postal address data discarded', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_nil contact.city + assert_nil contact.street + assert_nil contact.zip + assert_nil contact.country_code + end + + def test_requires_contact_address_when_processing_enabled + Setting.address_processing = true + + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2003, json[:code] + assert json[:message].include? 'param is missing or the value is empty' + + Setting.address_processing = false + end + + def test_validates_ident_code + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "123123123" + } + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2005, json[:code] + assert json[:message].include? 'Ident code does not conform to national identification number format' + end +end From 33f6da53c12f5bc18f3ab986a40a5c3e3de0225e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 13:58:21 +0300 Subject: [PATCH 059/202] REPP: Contact update test --- .../repp/v1/contacts_controller.rb | 16 ++-- app/models/actions/contact_update.rb | 2 +- .../repp/v1/contacts/update_test.rb | 82 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 test/integration/repp/v1/contacts/update_test.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 0b52505b0..1677e827f 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -41,7 +41,7 @@ module Repp ## PUT /repp/v1/contacts/1 def update - action = Actions::ContactUpdate.new(@contact, contact_params_with_address, + action = Actions::ContactUpdate.new(@contact, contact_params_with_address(required: false), params[:legal_document], contact_ident_params(required: false), current_user) @@ -84,16 +84,16 @@ module Repp @contact = Epp::Contact.find_by!(code: code, registrar: current_user.registrar) end - def contact_params_with_address - return contact_create_params unless contact_addr_params.key?(:addr) + def contact_params_with_address(required: true) + return contact_create_params(required: required) unless contact_addr_params.key?(:addr) addr = {} contact_addr_params[:addr].each_key { |k| addr[k] = contact_addr_params[:addr][k] } - contact_create_params.merge(addr) + contact_create_params(required: required).merge(addr) end - def contact_create_params - params.require(:contact).require(%i[name email phone]) + def contact_create_params(required: true) + params.require(:contact).require(%i[name email phone]) if required params.require(:contact).permit(:name, :email, :phone) end @@ -102,8 +102,10 @@ module Repp params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else - params.permit(ident: %i[ident ident_type ident_country_code]) + params.permit(contact: { ident: %i[ident ident_type ident_country_code] }) end + + params[:contact][:ident] end def contact_addr_params diff --git a/app/models/actions/contact_update.rb b/app/models/actions/contact_update.rb index f8b39ecb4..6400526c5 100644 --- a/app/models/actions/contact_update.rb +++ b/app/models/actions/contact_update.rb @@ -17,7 +17,7 @@ module Actions def call maybe_remove_address maybe_update_statuses - maybe_update_ident + maybe_update_ident if ident maybe_attach_legal_doc commit end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb new file mode 100644 index 000000000..816a90637 --- /dev/null +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -0,0 +1,82 @@ +require 'test_helper' + +class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest + def setup + @contact = contacts(:john) + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_contact + request_body = { + "contact": { + "email": "donaldtrump@yandex.ru" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_equal(request_body[:contact][:email], contact.email) + end + + def test_removes_postal_info_when_updated + request_body = { + "contact": { + "addr": { + "city": "Tallinn", + "street": "Wismari 13", + "zip": "12345", + "country_code": "EE" + } + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1100, json[:code] + assert_equal 'Command completed successfully; Postal address data discarded', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.present? + + assert_nil contact.city + assert_nil contact.street + assert_nil contact.zip + assert_nil contact.country_code + end + + def test_can_not_change_ident_code + request_body = { + "contact": { + "name": "Donald Trumpster", + "ident": { + "ident_type": "priv", + "ident_country_code": "US", + "ident": "12345" + } + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + @contact.reload + assert_not @contact.ident == 12345 + assert_response :bad_request + assert_equal 2308, json[:code] + assert json[:message].include? 'Ident update is not allowed. Consider creating new contact object' + end +end From ddc26b81b15cd293bf7d9080f49fdb0d6c6a7ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 15:36:40 +0300 Subject: [PATCH 060/202] REPP:: Contact create/update legaldoc test --- .../repp/v1/contacts_controller.rb | 7 +---- .../repp/v1/contacts/create_test.rb | 29 +++++++++++++++++++ .../repp/v1/contacts/update_test.rb | 22 ++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 1677e827f..0e1bd1791 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -28,7 +28,7 @@ module Repp ## POST /repp/v1/contacts def create @contact = Epp::Contact.new(contact_params_with_address, current_user.registrar, epp: false) - action = Actions::ContactCreate.new(@contact, params[:legal_documents], + action = Actions::ContactCreate.new(@contact, params[:legal_document], contact_ident_params) unless action.call @@ -116,11 +116,6 @@ module Repp params.require(:contact).permit(addr: %i[country_code city street zip]) end end - - def legal_document_params - params.require(:legal_document).require(%i[body type]) - params.require(:legal_document).permit(:body, :type) - end end end end diff --git a/test/integration/repp/v1/contacts/create_test.rb b/test/integration/repp/v1/contacts/create_test.rb index 30e71eee9..f30bc368f 100644 --- a/test/integration/repp/v1/contacts/create_test.rb +++ b/test/integration/repp/v1/contacts/create_test.rb @@ -124,4 +124,33 @@ class ReppV1ContactsCreateTest < ActionDispatch::IntegrationTest assert_equal 2005, json[:code] assert json[:message].include? 'Ident code does not conform to national identification number format' end + + def test_attaches_legaldoc_if_present + request_body = { + "contact": { + "name": "Donald Trump", + "phone": "+372.51111112", + "email": "donald@trumptower.com", + "ident": { + "ident_type": "priv", + "ident_country_code": "EE", + "ident": "39708290069" + }, + }, + "legal_document": { + "type": "pdf", + "body": "#{'test' * 2000}" + } + } + + post '/repp/v1/contacts', headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + contact = Contact.find_by(code: json[:data][:contact][:id]) + assert contact.legal_documents.any? + end end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb index 816a90637..dd762e341 100644 --- a/test/integration/repp/v1/contacts/update_test.rb +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -79,4 +79,26 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest assert_equal 2308, json[:code] assert json[:message].include? 'Ident update is not allowed. Consider creating new contact object' end + + def test_attaches_legaldoc_if_present + request_body = { + "contact": { + "email": "donaldtrump@yandex.ru" + }, + "legal_document": { + "type": "pdf", + "body": "#{'test' * 2000}" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + @contact.reload + assert @contact.legal_documents.any? + end end From 516b2180cab76a28b8552a4c8a132b9df9bd29a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 20 Oct 2020 17:00:25 +0300 Subject: [PATCH 061/202] REPP: Extend V1 base test --- app/controllers/repp/v1/base_controller.rb | 16 ++++------ test/integration/repp/v1/base_test.rb | 37 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 68cc8d225..678ae0a22 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -69,10 +69,6 @@ module Repp ) end - def ip_whitelisted? - return false unless current_user.registrar.api_ip_white?(request.ip) - end - def basic_token pattern = /^Basic / header = request.headers['Authorization'] @@ -95,16 +91,16 @@ module Repp end def check_ip_restriction - ip_restriction = Authorization::RestrictedIP.new(request.ip) - allowed = ip_restriction.can_access_registrar_area?(@current_user.registrar) + allowed = @current_user.registrar.api_ip_white?(request.ip) return if allowed render( - status: :unauthorized, - json: { errors: [ - { base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }, - ] } + json: { + code: 2202, + message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip), + }, + status: :unauthorized ) end diff --git a/test/integration/repp/v1/base_test.rb b/test/integration/repp/v1/base_test.rb index 63255dd24..d0baed30e 100644 --- a/test/integration/repp/v1/base_test.rb +++ b/test/integration/repp/v1/base_test.rb @@ -15,6 +15,15 @@ class ReppV1BaseTest < ActionDispatch::IntegrationTest assert_response :unauthorized assert_equal 'Invalid authorization information', response_json[:message] + + invalid_token = Base64.encode64("nonexistant:user") + headers = { 'Authorization' => "Basic #{invalid_token}" } + + get repp_v1_contacts_path, headers: headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] end def test_authenticates_valid_user @@ -23,4 +32,32 @@ class ReppV1BaseTest < ActionDispatch::IntegrationTest assert_response :ok end + + def test_processes_invalid_base64_token_format_properly + token = '??as8d9sf kjsdjh klsdfjjf' + headers = { 'Authorization' => "Basic #{token}"} + get repp_v1_contacts_path, headers: headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 'Invalid authorization information', response_json[:message] + end + + def test_takes_ip_whitelist_into_account + Setting.api_ip_whitelist_enabled = true + Setting.registrar_ip_whitelist_enabled = true + + whiteip = white_ips(:one) + whiteip.update(ipv4: '1.1.1.1') + + get repp_v1_contacts_path, headers: @auth_headers + response_json = JSON.parse(response.body, symbolize_names: true) + + assert_response :unauthorized + assert_equal 2202, response_json[:code] + assert response_json[:message].include? 'Access denied from IP' + + Setting.api_ip_whitelist_enabled = false + Setting.registrar_ip_whitelist_enabled = false + end end From f9bef781ab00a21cb5b08dc03156290ecf7828a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 11:54:25 +0300 Subject: [PATCH 062/202] REPP: Add contact:destroy action --- app/controllers/epp/contacts_controller.rb | 9 ++-- .../repp/v1/contacts_controller.rb | 12 ++++- app/models/actions/contact_delete.rb | 41 ++++++++++++++++ app/models/contact.rb | 25 ---------- .../repp/v1/contacts/delete_test.rb | 47 +++++++++++++++++++ 5 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 app/models/actions/contact_delete.rb create mode 100644 test/integration/repp/v1/contacts/delete_test.rb diff --git a/app/controllers/epp/contacts_controller.rb b/app/controllers/epp/contacts_controller.rb index 4ef8d11d3..85305213b 100644 --- a/app/controllers/epp/contacts_controller.rb +++ b/app/controllers/epp/contacts_controller.rb @@ -44,12 +44,13 @@ module Epp def delete authorize! :delete, @contact, @password - - if @contact.destroy_and_clean(params[:parsed_frame]) - render_epp_response '/epp/contacts/delete' - else + action = Actions::ContactDelete.new(@contact, params[:legal_document]) + unless action.call handle_errors(@contact) + return end + + render_epp_response '/epp/contacts/delete' end def renew diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 0e1bd1791..eea6767f7 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,7 +1,7 @@ module Repp module V1 class ContactsController < BaseController - before_action :find_contact, only: %i[show update] + before_action :find_contact, only: %i[show update destroy] ## GET /repp/v1/contacts def index @@ -53,6 +53,16 @@ module Repp render_success(create_update_success_body) end + def destroy + action = Actions::ContactDelete.new(@contact, params[:legal_document]) + unless action.call + handle_errors(@contact) + return + end + + render_success + end + def contact_addr_present? return false unless contact_addr_params.key?(:addr) diff --git a/app/models/actions/contact_delete.rb b/app/models/actions/contact_delete.rb new file mode 100644 index 000000000..59032d566 --- /dev/null +++ b/app/models/actions/contact_delete.rb @@ -0,0 +1,41 @@ +module Actions + class ContactDelete + attr_reader :contact + attr_reader :new_attributes + attr_reader :legal_document + attr_reader :ident + attr_reader :user + + def initialize(contact, legal_document = nil) + @legal_document = legal_document + @contact = contact + end + + def call + maybe_attach_legal_doc + + if contact.linked? + contact.errors.add(:domains, :exist) + return + end + + commit + end + + def maybe_attach_legal_doc + return unless legal_document + + document = contact.legal_documents.create( + document_type: legal_document[:type], + body: legal_document[:body] + ) + + contact.legal_document_id = document.id + contact.save + end + + def commit + contact.destroy + end + end +end diff --git a/app/models/contact.rb b/app/models/contact.rb index 9dc1e34a2..8a154c50c 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -333,31 +333,6 @@ class Contact < ApplicationRecord Country.new(country_code) 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 frame - if linked? - errors.add(:domains, :exist) - return false - end - - legal_document_data = ::Deserializers::Xml::LegalDocument.new(frame).call - - if legal_document_data - - doc = LegalDocument.create( - documentable_type: Contact, - document_type: legal_document_data[:type], - body: legal_document_data[:body] - ) - self.legal_documents = [doc] - self.legal_document_id = doc.id - self.save - end - destroy - end - def to_upcase_country_code self.ident_country_code = ident_country_code.upcase if ident_country_code self.country_code = country_code.upcase if country_code diff --git a/test/integration/repp/v1/contacts/delete_test.rb b/test/integration/repp/v1/contacts/delete_test.rb new file mode 100644 index 000000000..07438d8af --- /dev/null +++ b/test/integration/repp/v1/contacts/delete_test.rb @@ -0,0 +1,47 @@ +require 'test_helper' + +class ReppV1ContactsDeleteTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_deletes_unassociated_contact + contact = contacts(:invalid_email) + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + end + + def test_can_not_delete_associated_contact + contact = contacts(:john) + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2305, json[:code] + assert_equal 'Object association prohibits operation [domains]', json[:message] + end + + def test_handles_unknown_contact + delete "/repp/v1/contacts/definitely:unexistant", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + end + + def test_can_not_destroy_other_registrar_contact + contact = contacts(:jack) + + delete "/repp/v1/contacts/#{contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + end +end From 2a1967918cec277ec85889e7eb1ec3fe1b4617a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 12:31:51 +0300 Subject: [PATCH 063/202] REPP: Domains list test --- test/integration/repp/v1/domains/list_test.rb | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/integration/repp/v1/domains/list_test.rb diff --git a/test/integration/repp/v1/domains/list_test.rb b/test/integration/repp/v1/domains/list_test.rb new file mode 100644 index 000000000..bee390e5c --- /dev/null +++ b/test/integration/repp/v1/domains/list_test.rb @@ -0,0 +1,54 @@ +require 'test_helper' + +class ReppV1DomainsListTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_returns_registrar_domains + get repp_v1_domains_path, headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:domains].length + + assert json[:data][:domains][0].is_a? String + end + + def test_returns_detailed_registrar_domains + get repp_v1_domains_path(details: true), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal @user.registrar.domains.count, json[:data][:total_number_of_records] + assert_equal @user.registrar.domains.count, json[:data][:domains].length + + assert json[:data][:domains][0].is_a? Hash + end + + def test_respects_limit + get repp_v1_domains_path(details: true, limit: 2), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal 2, json[:data][:domains].length + end + + def test_respects_offset + offset = 1 + get repp_v1_domains_path(details: true, offset: offset), headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + + assert_equal (@user.registrar.domains.count - offset), json[:data][:domains].length + end +end From 9a36b0403ce8e32baa89f2fb03738e0ed2b73807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 13:10:28 +0300 Subject: [PATCH 064/202] REPP: Domain transfer info test --- app/controllers/repp/v1/domains_controller.rb | 2 +- .../repp/v1/domains/transfer_info_test.rb | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/domains/transfer_info_test.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 163731c69..652786488 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -90,7 +90,7 @@ module Repp return if @domain.transfer_code.eql?(request.headers['Auth-Code']) - @epp_errors << { code: '401', msg: I18n.t('errors.messages.epp_authorization_error') } + @epp_errors << { code: 2202, msg: I18n.t('errors.messages.epp_authorization_error') } handle_errors end diff --git a/test/integration/repp/v1/domains/transfer_info_test.rb b/test/integration/repp/v1/domains/transfer_info_test.rb new file mode 100644 index 000000000..6c64e24b9 --- /dev/null +++ b/test/integration/repp/v1/domains/transfer_info_test.rb @@ -0,0 +1,40 @@ +require 'test_helper' + +class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + @domain = domains(:shop) + @auth_headers = { 'Authorization' => token } + end + + def test_can_query_domain_info + headers = @auth_headers + headers['Auth-Code'] = @domain.transfer_code + + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.name, json[:data][:domain] + assert json[:data][:registrant].present? + assert json[:data][:admin_contacts].present? + assert json[:data][:tech_contacts].present? + end + + def test_respects_domain_authorization_code + headers = @auth_headers + headers['Auth-Code'] = 'jhfgifhdg' + + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2202, json[:code] + assert_equal 'Authorization error', json[:message] + assert_empty json[:data] + end +end From 774ada6ebb30f9cf7d1875ca9295ce23d2182beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 14:00:16 +0300 Subject: [PATCH 065/202] REPP: Domain transfer test --- app/controllers/repp/v1/domains_controller.rb | 3 +- .../repp/v1/domains/transfer_info_test.rb | 4 +- .../repp/v1/domains/transfer_test.rb | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/integration/repp/v1/domains/transfer_test.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 652786488..da2990ead 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -29,7 +29,7 @@ module Repp @errors ||= [] @successful = [] - params[:data][:domain_transfers].each do |transfer| + transfer_params[:domain_transfers].each do |transfer| initiate_transfer(transfer) end @@ -57,6 +57,7 @@ module Repp add_error("#{domain_name} transfer code is wrong") return end + domain end diff --git a/test/integration/repp/v1/domains/transfer_info_test.rb b/test/integration/repp/v1/domains/transfer_info_test.rb index 6c64e24b9..f57675b06 100644 --- a/test/integration/repp/v1/domains/transfer_info_test.rb +++ b/test/integration/repp/v1/domains/transfer_info_test.rb @@ -13,7 +13,7 @@ class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest headers = @auth_headers headers['Auth-Code'] = @domain.transfer_code - get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: headers json = JSON.parse(response.body, symbolize_names: true) assert_response :ok @@ -29,7 +29,7 @@ class ReppV1DomainsTransferInfoTest < ActionDispatch::IntegrationTest headers = @auth_headers headers['Auth-Code'] = 'jhfgifhdg' - get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: @auth_headers + get "/repp/v1/domains/#{@domain.name}/transfer_info", headers: headers json = JSON.parse(response.body, symbolize_names: true) assert_response :bad_request diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb new file mode 100644 index 000000000..4c1280b47 --- /dev/null +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + @domain = domains(:hospital) + + @auth_headers = { 'Authorization' => token } + end + + def test_transfers_domain + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal @domain.name, json[:data][0][:attributes][:domain_name] + assert_equal 'domain_transfer', json[:data][0][:type] + end + + def test_does_not_transfer_domain_with_invalid_auth_code + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": "sdfgsdfg" } + ] + } + } + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'Command failed', json[:message] + + assert_equal "#{@domain.name} transfer code is wrong", json[:data][0][:title] + end +end From 06ad8c8c77f16daa2eb4fa96cb11ef12694770a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 15:14:33 +0300 Subject: [PATCH 066/202] REPP: Domain tech contact replace test --- .../repp/v1/domains/contacts_controller.rb | 2 +- app/controllers/repp/v1/domains_controller.rb | 6 +- .../v1/domains/contact_replacement_test.rb | 65 +++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 test/integration/repp/v1/domains/contact_replacement_test.rb diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 70f64ed28..fb5dfb567 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -7,7 +7,7 @@ module Repp def set_current_contact @current_contact = current_user.registrar.contacts.find_by!( - code: params[:current_contact_id] + code: contact_params[:current_contact_id] ) end diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index da2990ead..7612b7d75 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -6,7 +6,7 @@ module Repp def index records = current_user.registrar.domains domains = records.limit(limit).offset(offset) - domains = domains.pluck(:name) unless params[:details] == 'true' + domains = domains.pluck(:name) unless index_params[:details] == 'true' render_success(data: { domains: domains, total_number_of_records: records.count }) end @@ -96,11 +96,11 @@ module Repp end def limit - params[:limit] || 200 + index_params[:limit] || 200 end def offset - params[:offset] || 0 + index_params[:offset] || 0 end def index_params diff --git a/test/integration/repp/v1/domains/contact_replacement_test.rb b/test/integration/repp/v1/domains/contact_replacement_test.rb new file mode 100644 index 000000000..3cbd9eb8e --- /dev/null +++ b/test/integration/repp/v1/domains/contact_replacement_test.rb @@ -0,0 +1,65 @@ +require 'test_helper' + +class ReppV1DomainsContactReplacementTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_replaces_tech_contact_with_new_one + replaceable_contact = contacts(:william) + replacing_contact = contacts(:jane) + + payload = { + "current_contact_id": replaceable_contact.code, + "new_contact_id": replacing_contact.code + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert json[:data][:affected_domains].include? 'airport.test' + assert json[:data][:affected_domains].include? 'shop.test' + + assert_empty json[:data][:skipped_domains] + end + + def test_tech_contact_id_must_differ + replaceable_contact = contacts(:william) + replacing_contact = contacts(:william) + + payload = { + "current_contact_id": replaceable_contact.code, + "new_contact_id": replacing_contact.code + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'New contact must be different from current', json[:message] + end + + def test_contact_codes_must_be_valid + payload = { + "current_contact_id": 'dfgsdfg', + "new_contact_id": 'vvv' + } + + patch '/repp/v1/domains/contacts', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + +end From a43df5ca95610f5b89ec262270147e415f0d4a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 15:34:16 +0300 Subject: [PATCH 067/202] REPP: Accounts balance test --- .../repp/v1/accounts_controller.rb | 2 +- .../repp/v1/accounts/balance_test.rb | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/integration/repp/v1/accounts/balance_test.rb diff --git a/app/controllers/repp/v1/accounts_controller.rb b/app/controllers/repp/v1/accounts_controller.rb index 5adf99ea1..89c14808f 100644 --- a/app/controllers/repp/v1/accounts_controller.rb +++ b/app/controllers/repp/v1/accounts_controller.rb @@ -4,7 +4,7 @@ module Repp def balance resp = { balance: current_user.registrar.cash_account.balance, currency: current_user.registrar.cash_account.currency } - render(json: resp, status: :ok) + render_success(data: resp) end end end diff --git a/test/integration/repp/v1/accounts/balance_test.rb b/test/integration/repp/v1/accounts/balance_test.rb new file mode 100644 index 000000000..785e0aee8 --- /dev/null +++ b/test/integration/repp/v1/accounts/balance_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class ReppV1BalanceTest < ActionDispatch::IntegrationTest + def setup + @registrar = users(:api_bestnames) + token = Base64.encode64("#{@registrar.username}:#{@registrar.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_can_query_balance + get '/repp/v1/accounts/balance', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @registrar.registrar.cash_account.balance.to_s, json[:data][:balance] + assert_equal @registrar.registrar.cash_account.currency, json[:data][:currency] + end +end From e9c0d09b9fabebce56e9dee171ed023570f0849c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 21 Oct 2020 16:05:26 +0300 Subject: [PATCH 068/202] REPP: Registrar nameserver replacement test --- .../repp/v1/registrar/nameservers_test.rb | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/integration/repp/v1/registrar/nameservers_test.rb diff --git a/test/integration/repp/v1/registrar/nameservers_test.rb b/test/integration/repp/v1/registrar/nameservers_test.rb new file mode 100644 index 000000000..f01769dfb --- /dev/null +++ b/test/integration/repp/v1/registrar/nameservers_test.rb @@ -0,0 +1,77 @@ +require 'test_helper' + +class ReppV1RegistrarNameserversTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + end + + def test_updates_nameserver_values + nameserver = nameservers(:shop_ns1) + payload = { + "data": { + "id": nameserver.hostname, + "type": "nameserver", + "attributes": { + "hostname": "#{nameserver.hostname}.test", + "ipv4": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal({ hostname: "#{nameserver.hostname}.test", ipv4: ["1.1.1.1"] }, json[:data][:attributes]) + assert_equal({ hostname: "#{nameserver.hostname}.test", ipv4: ["1.1.1.1"] }, json[:data][:attributes]) + assert json[:data][:affected_domains].include? 'airport.test' + assert json[:data][:affected_domains].include? 'shop.test' + end + + def test_nameserver_with_hostname_must_exist + payload = { + "data": { + "id": 'ns.nonexistant.test', + "type": "nameserver", + "attributes": { + "hostname": "ns1.dn.test", + "ipv4": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_ip_must_be_in_correct_format + nameserver = nameservers(:shop_ns1) + payload = { + "data": { + "id": nameserver.hostname, + "type": "nameserver", + "attributes": { + "hostname": "#{nameserver.hostname}.test", + "ipv6": ["1.1.1.1"] + } + } + } + + put '/repp/v1/registrar/nameservers', headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2005, json[:code] + assert_equal 'IPv6 is invalid [ipv6]', json[:message] + end +end From 03d940a6950f55f3c64afa1d60b50bd93ed7af64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 15:25:02 +0300 Subject: [PATCH 069/202] REPP: Fix bulk domain transfer --- app/controllers/repp/v1/domains_controller.rb | 31 ++------ app/models/actions/domain_transfer.rb | 77 +++++++++++++++++++ .../repp/v1/domains/transfer_test.rb | 15 ++-- 3 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 app/models/actions/domain_transfer.rb diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 7612b7d75..7e81fac55 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,37 +33,16 @@ module Repp initiate_transfer(transfer) end - if @errors.any? - render_epp_error(:bad_request, @errors) - else - render_success(data: @successful) - end + render_success(data: {success: @successful, failed: @errors}) end def initiate_transfer(transfer) - domain = transferable_domain(transfer[:domain_name], transfer[:transfer_code]) - return unless domain + domain = Epp::Domain.find_or_initialize_by(name: transfer[:domain_name]) + action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], current_user.registrar) - DomainTransfer.request(domain, current_user.registrar) - @successful << { type: 'domain_transfer', attributes: { domain_name: domain.name } } - end + @successful << { type: 'domain_transfer', domain_name: domain.name } and return if action.call - def transferable_domain(domain_name, transfer_code) - domain = Domain.find_by(name: domain_name) - # rubocop:disable Style/AndOr - add_error("#{domain_name} does not exist") and return unless domain - # rubocop:enable Style/AndOr - unless domain.transfer_code.eql?(transfer_code) - add_error("#{domain_name} transfer code is wrong") - return - end - - domain - end - - def add_error(msg) - @errors ||= [] - @errors << { title: msg } + @errors << { type: 'domain_transfer', domain_name: domain.name, errors: domain.errors[:epp_errors] } end private diff --git a/app/models/actions/domain_transfer.rb b/app/models/actions/domain_transfer.rb new file mode 100644 index 000000000..620e9b4db --- /dev/null +++ b/app/models/actions/domain_transfer.rb @@ -0,0 +1,77 @@ +module Actions + class DomainTransfer + attr_reader :domain + attr_reader :transfer_code + attr_reader :legal_document + attr_reader :ident + attr_reader :user + + def initialize(domain, transfer_code, user) + @domain = domain + @transfer_code = transfer_code + @user = user + end + + def call + return unless domain_exists? + return unless run_validations + + #return domain.pending_transfer if domain.pending_transfer + #attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call) + + return if domain.errors[:epp_errors].any? + + commit + end + + def domain_exists? + return true if domain.persisted? + + domain.add_epp_error('2303', nil, nil, 'Object does not exist') + + false + end + + def run_validations + return unless validate_transfer_code + return unless validate_registrar + return unless validate_eligilibty + return unless validate_not_discarded + + true + end + + def validate_transfer_code + return true if transfer_code == domain.transfer_code + + domain.add_epp_error('2202', nil, nil, 'Invalid authorization information') + false + end + + def validate_registrar + return true unless user == domain.registrar + + domain.add_epp_error('2002', nil, nil, I18n.t(:domain_already_belongs_to_the_querying_registrar)) + false + end + + def validate_eligilibty + return true unless domain.non_transferable? + + domain.add_epp_error('2304', nil, nil, 'Domain is not transferable??') + false + end + + def validate_not_discarded + return true unless domain.discarded? + + domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer') + false + end + + def commit + bare_domain = Domain.find(domain.id) + ::DomainTransfer.request(bare_domain, user) + end + end +end diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb index 4c1280b47..9d2518117 100644 --- a/test/integration/repp/v1/domains/transfer_test.rb +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -25,8 +25,11 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert_equal @domain.name, json[:data][0][:attributes][:domain_name] - assert_equal 'domain_transfer', json[:data][0][:type] + assert_equal @domain.name, json[:data][:success][0][:domain_name] + + @domain.reload + + assert @domain.registrar = @user.registrar end def test_does_not_transfer_domain_with_invalid_auth_code @@ -40,10 +43,10 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload json = JSON.parse(response.body, symbolize_names: true) - assert_response :bad_request - assert_equal 2304, json[:code] - assert_equal 'Command failed', json[:message] + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] - assert_equal "#{@domain.name} transfer code is wrong", json[:data][0][:title] + assert_equal "Invalid authorization information", json[:data][:failed][0][:errors][0][:msg] end end From f2983da14cce0935d406e13b2a6e9ae47b88576f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 15:31:37 +0300 Subject: [PATCH 070/202] REPP: Fix CC issues --- app/controllers/repp/v1/domains_controller.rb | 14 +++++--- app/models/actions/domain_transfer.rb | 33 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/app/controllers/repp/v1/domains_controller.rb b/app/controllers/repp/v1/domains_controller.rb index 7e81fac55..ba90f23f2 100644 --- a/app/controllers/repp/v1/domains_controller.rb +++ b/app/controllers/repp/v1/domains_controller.rb @@ -33,16 +33,20 @@ module Repp initiate_transfer(transfer) end - render_success(data: {success: @successful, failed: @errors}) + render_success(data: { success: @successful, failed: @errors }) end def initiate_transfer(transfer) domain = Epp::Domain.find_or_initialize_by(name: transfer[:domain_name]) - action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], current_user.registrar) + action = Actions::DomainTransfer.new(domain, transfer[:transfer_code], + current_user.registrar) - @successful << { type: 'domain_transfer', domain_name: domain.name } and return if action.call - - @errors << { type: 'domain_transfer', domain_name: domain.name, errors: domain.errors[:epp_errors] } + if action.call + @successful << { type: 'domain_transfer', domain_name: domain.name } + else + @errors << { type: 'domain_transfer', domain_name: domain.name, + errors: domain.errors[:epp_errors] } + end end private diff --git a/app/models/actions/domain_transfer.rb b/app/models/actions/domain_transfer.rb index 620e9b4db..1ff9aafe9 100644 --- a/app/models/actions/domain_transfer.rb +++ b/app/models/actions/domain_transfer.rb @@ -14,10 +14,12 @@ module Actions def call return unless domain_exists? - return unless run_validations + return unless valid_transfer_code? - #return domain.pending_transfer if domain.pending_transfer - #attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call) + run_validations + + # return domain.pending_transfer if domain.pending_transfer + # attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call) return if domain.errors[:epp_errors].any? @@ -33,15 +35,12 @@ module Actions end def run_validations - return unless validate_transfer_code - return unless validate_registrar - return unless validate_eligilibty - return unless validate_not_discarded - - true + validate_registrar + validate_eligilibty + validate_not_discarded end - def validate_transfer_code + def valid_transfer_code? return true if transfer_code == domain.transfer_code domain.add_epp_error('2202', nil, nil, 'Invalid authorization information') @@ -49,24 +48,22 @@ module Actions end def validate_registrar - return true unless user == domain.registrar + return unless user == domain.registrar - domain.add_epp_error('2002', nil, nil, I18n.t(:domain_already_belongs_to_the_querying_registrar)) - false + domain.add_epp_error('2002', nil, nil, + I18n.t(:domain_already_belongs_to_the_querying_registrar)) end def validate_eligilibty - return true unless domain.non_transferable? + return unless domain.non_transferable? - domain.add_epp_error('2304', nil, nil, 'Domain is not transferable??') - false + domain.add_epp_error('2304', nil, nil, 'Object status prohibits operation') end def validate_not_discarded - return true unless domain.discarded? + return unless domain.discarded? domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer') - false end def commit From 8790d1415a3e3c1f2abfce2aecab82a7fc78eab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 22 Oct 2020 16:18:07 +0300 Subject: [PATCH 071/202] REPP: Extend domain transfer test --- test/integration/api/domain_transfers_test.rb | 43 ----------- .../repp/v1/domains/transfer_test.rb | 75 +++++++++++++++++++ 2 files changed, 75 insertions(+), 43 deletions(-) diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index ecb69d262..3e9c10100 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -12,26 +12,6 @@ class APIDomainTransfersTest < ApplicationIntegrationTest Setting.transfer_wait_time = @original_transfer_wait_time end - def test_returns_domain_transfers - post '/repp/v1/domains/transfer', params: request_params, as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 200 - - expected_body = { - code: 1000, - message: 'Command completed successfully', - data: [ - { - type: 'domain_transfer', - attributes: { domain_name: 'shop.test' }, - } - ] - } - - real_body = JSON.parse(response.body, symbolize_names: true) - assert_equal(expected_body, real_body) - end - def test_creates_new_domain_transfer assert_difference -> { @domain.transfers.size } do post '/repp/v1/domains/transfer', params: request_params, as: :json, @@ -83,29 +63,6 @@ class APIDomainTransfersTest < ApplicationIntegrationTest assert_equal 1, @new_registrar.contacts.where(name: 'William').size end - def test_fails_if_domain_does_not_exist - post '/repp/v1/domains/transfer', - params: { data: { domain_transfers: [{ domain_name: 'non-existent.test', - transfer_code: 'any' }] } }, - as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 400 - assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'non-existent.test does not exist' }] }), - JSON.parse(response.body, symbolize_names: true) - end - - def test_fails_if_transfer_code_is_wrong - post '/repp/v1/domains/transfer', - params: { data: { domain_transfers: [{ domain_name: 'shop.test', - transfer_code: 'wrong' }] } }, - as: :json, - headers: { 'HTTP_AUTHORIZATION' => http_auth_key } - assert_response 400 - refute_equal @new_registrar, @domain.registrar - assert_equal ({ code: 2304, message: 'Command failed', data: [{ title: 'shop.test transfer code is wrong' }] }), - JSON.parse(response.body, symbolize_names: true) - end - private def request_params diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb index 9d2518117..46d5c30c4 100644 --- a/test/integration/repp/v1/domains/transfer_test.rb +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -32,6 +32,31 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert @domain.registrar = @user.registrar end + def test_does_not_transfer_domain_if_not_transferable + @domain.schedule_force_delete(type: :fast_track) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Object status prohibits operation', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert_not @domain.registrar == @user.registrar + end + def test_does_not_transfer_domain_with_invalid_auth_code payload = { "data": { @@ -49,4 +74,54 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest assert_equal "Invalid authorization information", json[:data][:failed][0][:errors][0][:msg] end + + def test_does_not_transfer_domain_to_same_registrar + @domain.update!(registrar: @user.registrar) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Domain already belongs to the querying registrar', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert @domain.registrar == @user.registrar + end + + def test_does_not_transfer_domain_if_discarded + @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) + + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert_equal 'Object is not eligible for transfer', json[:data][:failed][0][:errors][0][:msg] + + @domain.reload + + assert_not @domain.registrar == @user.registrar + end end From cfd8e8b7f130cadfdbe0f8e72ef368fd20dbe4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 12:13:15 +0200 Subject: [PATCH 072/202] Docs: Update REPP account --- doc/repp/v1/account.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/repp/v1/account.md b/doc/repp/v1/account.md index 511c51382..80afabcdb 100644 --- a/doc/repp/v1/account.md +++ b/doc/repp/v1/account.md @@ -19,7 +19,11 @@ Content-Length: 37 Content-Type: application/json { - "balance": "324.45", - "currency": "EUR" + "code": 1000, + "message": "Command completed successfully", + "data": { + "balance": "356.0", + "currency": "EUR" + } } ``` From 4e895d9124bc24bd48cce643f0a46a55e8ed6e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 12:59:04 +0200 Subject: [PATCH 073/202] Docs: Update REPP domain --- doc/repp/v1/domain.md | 204 ++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 95 deletions(-) diff --git a/doc/repp/v1/domain.md b/doc/repp/v1/domain.md index c6734cbe2..20607f3b9 100644 --- a/doc/repp/v1/domain.md +++ b/doc/repp/v1/domain.md @@ -25,44 +25,52 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 808 Content-Type: application/json { - "domains": [ - { - "id": 1, - "name": "domain0.ee", - "registrar_id": 2, - "registered_at": "2015-09-09T09:11:14.861Z", - "status": null, - "valid_from": "2015-09-09T09:11:14.861Z", - "valid_to": "2016-09-09T09:11:14.861Z", - "registrant_id": 1, - "transfer_code": "98oiewslkfkd", - "created_at": "2015-09-09T09:11:14.861Z", - "updated_at": "2015-09-09T09:11:14.860Z", - "name_dirty": "domain0.ee", - "name_puny": "domain0.ee", - "period": 1, - "period_unit": "y", - "creator_str": null, - "updator_str": null, - "outzone_at": "2016-09-24T09:11:14.861Z", - "delete_date": "2016-10-24", - "registrant_verification_asked_at": null, - "registrant_verification_token": null, - "pending_json": { - }, - "force_delete_date": null, - "statuses": [ - "ok" - ], - "status_notes": { + "code": 1000, + "message": "Command completed successfully", + "data": { + "domains": [ + { + "id": 7, + "name": "private.ee", + "registrar_id": 2, + "valid_to": "2022-09-23T00:00:00.000+03:00", + "registrant_id": 11, + "created_at": "2020-09-22T14:16:47.420+03:00", + "updated_at": "2020-10-21T13:31:43.733+03:00", + "name_dirty": "private.ee", + "name_puny": "private.ee", + "period": 1, + "period_unit": "y", + "creator_str": "2-ApiUser: test", + "updator_str": null, + "outzone_at": null, + "delete_date": null, + "registrant_verification_asked_at": null, + "registrant_verification_token": null, + "pending_json": {}, + "force_delete_date": null, + "statuses": [ + "serverRenewProhibited" + ], + "status_notes": { + "ok": "", + "serverRenewProhibited": "" + }, + "upid": null, + "up_date": null, + "uuid": "6b6affa7-1449-4bd8-acf5-8b4752406705", + "locked_by_registrant_at": null, + "force_delete_start": null, + "force_delete_data": null, + "auth_info": "367b1e6d1f0d9aa190971ad8f571cd4d", + "valid_from": "2020-09-22T14:16:47.420+03:00" } - } - ], - "total_number_of_records": 2 + ], + "total_number_of_records": 10 + } } ``` @@ -83,14 +91,17 @@ Content-Type: application/json ``` HTTP/1.1 200 Cache-Control: max-age=0, private, must-revalidate -Content-Length: 54 Content-Type: application/json { - "domains": [ - "domain1.ee" - ], - "total_number_of_records": 2 + "code": 1000, + "message": "Command completed successfully", + "data": { + "domains": [ + "private.ee", + ], + "total_number_of_records": 1 + } } ``` @@ -117,65 +128,68 @@ Please note that domain transfer/authorisation code must be placed in header - * ``` HTTP/1.1 200 OK Cache-Control: max-age=0, private, must-revalidate -Content-Length: 784 Content-Type: application/json - { - "domain":"ee-test.ee", - "registrant":{ - "code":"EE:R1", - "name":"Registrant", - "ident":"17612535", - "ident_type":"org", - "ident_country_code":"EE", - "phone":"+372.1234567", - "email":"registrant@cache.ee", - "street":"Businesstreet 1", - "city":"Tallinn", - "zip":"10101", - "country_code":"EE", - "statuses":[ - "ok", - "linked" - ] - }, - "admin_contacts":[ - { - "code":"EE:A1", - "name":"Admin Contact", - "ident":"17612535376", - "ident_type":"priv", - "ident_country_code":"EE", - "phone":"+372.7654321", - "email":"admin@cache.ee", - "street":"Adminstreet 2", - "city":"Tallinn", - "zip":"12345", - "country_code":"EE", - "statuses":[ - "ok", - "linked" + "code": 1000, + "message": "Command completed successfully", + "data": { + "domain":"ee-test.ee", + "registrant":{ + "code":"EE:R1", + "name":"Registrant", + "ident":"17612535", + "ident_type":"org", + "ident_country_code":"EE", + "phone":"+372.1234567", + "email":"registrant@cache.ee", + "street":"Businesstreet 1", + "city":"Tallinn", + "zip":"10101", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + }, + "admin_contacts":[ + { + "code":"EE:A1", + "name":"Admin Contact", + "ident":"17612535376", + "ident_type":"priv", + "ident_country_code":"EE", + "phone":"+372.7654321", + "email":"admin@cache.ee", + "street":"Adminstreet 2", + "city":"Tallinn", + "zip":"12345", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + } + ], + "tech_contacts":[ + { + "code":"EE:T1", + "name":"Tech Contact", + "ident":"17612536", + "ident_type":"org", + "ident_country_code":"EE", + "phone":"+372.7654321", + "email":"tech@cache.ee", + "street":"Techstreet 1", + "city":"Tallinn", + "zip":"12345", + "country_code":"EE", + "statuses":[ + "ok", + "linked" + ] + } ] } - ], - "tech_contacts":[ - { - "code":"EE:T1", - "name":"Tech Contact", - "ident":"17612536", - "ident_type":"org", - "ident_country_code":"EE", - "phone":"+372.7654321", - "email":"tech@cache.ee", - "street":"Techstreet 1", - "city":"Tallinn", - "zip":"12345", - "country_code":"EE", - "statuses":[ - "ok", - "linked" - ] - } - ] + } } ``` From 09c62fd7ebb1e13f9c4eb71c34425eb38f373f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 13:40:11 +0200 Subject: [PATCH 074/202] Docs: Update REPP domain transfer --- doc/repp/v1/domain_transfers.md | 70 ++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/doc/repp/v1/domain_transfers.md b/doc/repp/v1/domain_transfers.md index a6eb4683c..a11c0a852 100644 --- a/doc/repp/v1/domain_transfers.md +++ b/doc/repp/v1/domain_transfers.md @@ -1,28 +1,28 @@ # Domain transfers -## POST /repp/v1/domain_transfers +## POST /repp/v1/domains/transfer Transfers domains. #### Request ``` -POST /repp/v1/domain_transfers +POST /repp/v1/domains/transfer Accept: application/json Content-Type: application/json Authorization: Basic dGVzdDp0ZXN0dGVzdA== { - "data":{ - "domainTransfers":[ - { - "domainName":"example.com", - "transferCode":"63e7" - }, - { - "domainName":"example.org", - "transferCode":"15f9" - } - ] - } + "data": { + "domain_transfers": [ + { + "domain_name":"example.com", + "transferCode":"63e7" + }, + { + "domain_name":"example.org", + "transferCode":"15f9" + } + ] + } } ``` @@ -31,14 +31,21 @@ Authorization: Basic dGVzdDp0ZXN0dGVzdA== HTTP/1.1 200 Content-Type: application/json { - "data":[ + "code": 1000, + "message": "Command completed successfully", + "data": { + "success": [ { - "type":"domain_transfer" + "type": "domain_transfer", + "domain_name": "example.com" }, { - "type":"domain_transfer" + "type": "domain_transfer", + "domain_name": "example.org" } - ] + ], + "failed": [] + } } ``` @@ -48,13 +55,32 @@ Content-Type: application/json HTTP/1.1 400 Content-Type: application/json { - "errors":[ + "code": 1000, + "message": "Command completed successfully", + "data": { + "success": [], + "failed": [ { - "title":"example.com transfer code is wrong" + "type": "domain_transfer", + "domain_name": "example.com", + "errors": [ + { + "code": "2202", + "msg": "Invalid authorization information" + } + ] }, { - "title":"example.org does not exist" + "type": "domain_transfer", + "domain_name": "example.org", + "errors": [ + { + "code": "2304", + "msg": "Object status prohibits operation" + } + ] } - ] + ] + } } ``` From 909460668de0052b5fcbcdeeddf44dd7844055ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 14:35:47 +0200 Subject: [PATCH 075/202] Docs: REPP Domain tech contact replace --- doc/repp/v1/domain_contacts.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/repp/v1/domain_contacts.md b/doc/repp/v1/domain_contacts.md index b412e9f73..2e542bf81 100644 --- a/doc/repp/v1/domain_contacts.md +++ b/doc/repp/v1/domain_contacts.md @@ -5,15 +5,26 @@ Replaces all domain contacts of the current registrar. ### Example request ``` -$ curl https://repp.internet.ee/v1/domains/contacts \ - -X PATCH \ - -u username:password \ - -d current_contact_id=foo \ - -d new_contact_id=bar +PATCH /repp/v1/domains/contacts HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Basic dGVzdDp0ZXN0dGVzdA== + +{ + "current_contact_id": "ATSAA:749AA80F", + "new_contact_id": "ATSAA:E36957D7" +} ``` ### Example response ``` { - "affected_domains": ["example.com", "example.org"] + "code": 1000, + "message": "Command completed successfully", + "data": { + "affected_domains": [ + "private.ee", + ], + "skipped_domains": [] + } } ``` From d2d7bfb3862cd3397f72131042d9ccf20d93062c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 14:55:30 +0200 Subject: [PATCH 076/202] Docs: REPP registrar nameserver replacement --- doc/repp/v1/nameservers.md | 50 +++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/doc/repp/v1/nameservers.md b/doc/repp/v1/nameservers.md index 8190530d7..ab53c72df 100644 --- a/doc/repp/v1/nameservers.md +++ b/doc/repp/v1/nameservers.md @@ -10,15 +10,15 @@ Accept: application/json Content-Type: application/json Authorization: Basic dGVzdDp0ZXN0dGVzdA== { - "data":{ - "type": "nameserver", - "id": "ns1.example.com", - "attributes": { - "hostname": "new-ns1.example.com", - "ipv4": ["192.0.2.1", "192.0.2.2"], - "ipv6": ["2001:db8::1", "2001:db8::2"] - }, + "data": { + "type": "nameserver", + "id": "ns1.example.com", + "attributes": { + "hostname": "new-ns1.example.com", + "ipv4": ["192.0.2.1", "192.0.2.2"], + "ipv6": ["2001:db8::1", "2001:db8::2"] } + } } ``` @@ -27,16 +27,26 @@ Authorization: Basic dGVzdDp0ZXN0dGVzdA== HTTP/1.1 200 Content-Type: application/json { - "data":{ + "code": 1000, + "message": "Command completed successfully", + "data": { "type": "nameserver", "id": "new-ns1.example.com", "attributes": { "hostname": "new-ns1.example.com", - "ipv4": ["192.0.2.1", "192.0.2.2"], - "ipv6": ["2001:db8::1", "2001:db8::2"] - } - }, - "affected_domains": ["example.com", "example.org"] + "ipv4": [ + "192.0.2.1", + "192.0.2.2" + ], + "ipv6": [ + "2001:db8::1", + "2001:db8::2" + ] + }, + "affected_domains": [ + "private.ee" + ] + } } ``` @@ -44,14 +54,10 @@ Content-Type: application/json ``` HTTP/1.1 400 Content-Type: application/json + { - "errors":[ - { - "title":"ns1.example.com does not exist" - }, - { - "title":"192.0.2.1 is not a valid IPv4 address" - } - ] + "code": 2005, + "message": "IPv4 is invalid [ipv4]", + "data": {} } ``` From 7fd31b7004ee3d6ec68e6d4d9f169b4623f140eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 15:12:40 +0200 Subject: [PATCH 077/202] Docs: REPP contact management --- doc/repp/v1/contact.md | 138 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/doc/repp/v1/contact.md b/doc/repp/v1/contact.md index 41f45551f..aa6904b72 100644 --- a/doc/repp/v1/contact.md +++ b/doc/repp/v1/contact.md @@ -88,3 +88,141 @@ Content-Type: application/json "total_number_of_records": 2 } ``` + +## POST /repp/v1/contacts +Creates new contact + + +#### Request +``` +POST /repp/v1/contacts HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json + +{ + "contact": { + "name": "John Doe", + "email": "john@doe.com", + "phone": "+371.1234567", + "ident": { + "ident": "12345678901", + "ident_type": "priv", + "ident_country_code": "EE" + } + } +} +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": { + "contact": { + "id": "ATSAA:20DCDCA1" + } + } +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2005, + "message": "Ident code does not conform to national identification number format of Estonia", + "data": {} +} +``` + +## PUT /repp/v1/contacts/**contact id** +Updates existing contact + + +#### Request +``` +PUT /repp/v1/contacts/ATSAA:9CD5F321 HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json + +{ + "contact": { + "phone": "+372.123123123" + } +} +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": { + "contact": { + "id": "ATSAA:20DCDCA1" + } + } +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2005, + "message": "Phone nr is invalid [phone]", + "data": {} +} +``` + +## DELETE /repp/v1/contacts/**contact id** +Deletes existing contact + + +#### Request +``` +DELETE /repp/v1/contacts/ATSAA:9CD5F321 HTTP/1.1 +Authorization: Basic dGVzdDp0ZXN0MTIz +Content-Type: application/json +``` + +#### Response +``` +HTTP/1.1 200 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 1000, + "message": "Command completed successfully", + "data": {} +} +``` + +#### Failed response +``` +HTTP/1.1 400 +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/json + +{ + "code": 2305, + "message": "Object association prohibits operation [domains]", + "data": {} +} +``` From 8bc064a0b80abe8b190def7f9bbeea98f311b056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 26 Oct 2020 17:09:12 +0200 Subject: [PATCH 078/202] Fix contact create with predefined code --- app/controllers/repp/v1/contacts_controller.rb | 2 +- app/models/epp/contact.rb | 2 +- lib/deserializers/xml/contact.rb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index eea6767f7..a6cd7d07d 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -104,7 +104,7 @@ module Repp def contact_create_params(required: true) params.require(:contact).require(%i[name email phone]) if required - params.require(:contact).permit(:name, :email, :phone) + params.require(:contact).permit(:name, :email, :phone, :code) end def contact_ident_params(required: true) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 50ebac065..3f0f3e8ab 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -36,7 +36,7 @@ class Epp::Contact < Contact attrs = epp ? attrs_from(frame, new_record: true) : frame super( attrs.merge( - code: epp ? frame.css('id').text : frame[:id], + code: epp ? frame.css('id').text : frame[:code], registrar: registrar ) ) diff --git a/lib/deserializers/xml/contact.rb b/lib/deserializers/xml/contact.rb index 4dd29c683..7c8404916 100644 --- a/lib/deserializers/xml/contact.rb +++ b/lib/deserializers/xml/contact.rb @@ -14,6 +14,7 @@ module Deserializers email: if_present('email'), fax: if_present('fax'), phone: if_present('voice'), + id: if_present('id'), # Address fields city: if_present('postalInfo addr city'), From 461b7c1d9c4eb9a180db665833ae8f221f0feefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 27 Oct 2020 12:28:35 +0200 Subject: [PATCH 079/202] Fix EPP contact update --- app/models/actions/contact_create.rb | 1 + lib/deserializers/xml/contact.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/actions/contact_create.rb b/app/models/actions/contact_create.rb index 13162a553..22fabc7b9 100644 --- a/app/models/actions/contact_create.rb +++ b/app/models/actions/contact_create.rb @@ -71,6 +71,7 @@ module Actions end def commit + contact.id = nil # new record return false if @error contact.generate_code diff --git a/lib/deserializers/xml/contact.rb b/lib/deserializers/xml/contact.rb index 7c8404916..4dd29c683 100644 --- a/lib/deserializers/xml/contact.rb +++ b/lib/deserializers/xml/contact.rb @@ -14,7 +14,6 @@ module Deserializers email: if_present('email'), fax: if_present('fax'), phone: if_present('voice'), - id: if_present('id'), # Address fields city: if_present('postalInfo addr city'), From 4b980a07f52a7f785a74facdf97211d07657dab9 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 9 Nov 2020 17:26:07 +0500 Subject: [PATCH 080/202] Add basic interactor/organizer support --- Gemfile | 2 ++ Gemfile.lock | 6 ++++ app/interactors/domain/force_delete/base.rb | 16 +++++++++ .../domain/force_delete/check_discarded.rb | 14 ++++++++ .../domain/force_delete/post_set_process.rb | 21 +++++++++++ .../domain/force_delete/prepare_domain.rb | 14 ++++++++ .../domain/force_delete/set_status.rb | 36 +++++++++++++++++++ app/models/concerns/domain/force_delete.rb | 4 +++ 8 files changed, 113 insertions(+) create mode 100644 app/interactors/domain/force_delete/base.rb create mode 100644 app/interactors/domain/force_delete/check_discarded.rb create mode 100644 app/interactors/domain/force_delete/post_set_process.rb create mode 100644 app/interactors/domain/force_delete/prepare_domain.rb create mode 100644 app/interactors/domain/force_delete/set_status.rb diff --git a/Gemfile b/Gemfile index 25c3eafff..c4f5a7cf8 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,8 @@ source 'https://rubygems.org' # core gem 'bootsnap', '>= 1.1.0', require: false +gem 'interactor', '~> 3.0' +gem 'interactor-rails', '~> 2.0' gem 'iso8601', '0.12.1' # for dates and times gem 'rails', '~> 6.0' gem 'rest-client' diff --git a/Gemfile.lock b/Gemfile.lock index c628257a2..cc59a8f41 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -251,6 +251,10 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) i18n_data (0.10.0) + interactor (3.1.2) + interactor-rails (2.2.1) + interactor (~> 3.0) + rails (>= 4.2) isikukood (0.1.2) iso8601 (0.12.1) jquery-rails (4.4.0) @@ -544,6 +548,8 @@ DEPENDENCIES figaro (= 1.1.1) grape haml (~> 5.0) + interactor (~> 3.0) + interactor-rails (~> 2.0) isikukood iso8601 (= 0.12.1) jquery-rails diff --git a/app/interactors/domain/force_delete/base.rb b/app/interactors/domain/force_delete/base.rb new file mode 100644 index 000000000..79de3cf2f --- /dev/null +++ b/app/interactors/domain/force_delete/base.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDelete + class Base + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDelete::CheckDiscarded, + Domain::ForceDelete::PrepareDomain, + Domain::ForceDelete::SetStatus, + Domain::ForceDelete::PostSetProcess, + Domain::ForceDelete::NotifyRegistrar, + Domain::ForceDelete::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete/check_discarded.rb new file mode 100644 index 000000000..a13c16778 --- /dev/null +++ b/app/interactors/domain/force_delete/check_discarded.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDelete + class CheckDiscarded + include Interactor + + def call + return unless context.domain.discarded? + + raise StandardError, + 'Force delete procedure cannot be scheduled while a domain is discarded' + end + end + end +end diff --git a/app/interactors/domain/force_delete/post_set_process.rb b/app/interactors/domain/force_delete/post_set_process.rb new file mode 100644 index 000000000..a2fc531b2 --- /dev/null +++ b/app/interactors/domain/force_delete/post_set_process.rb @@ -0,0 +1,21 @@ +class Domain + module ForceDelete + class PostSetProcess + include Interactor + + def call + statuses = context.domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + context.domain.save(validate: false) + end + end + end +end diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete/prepare_domain.rb new file mode 100644 index 000000000..4b3b83064 --- /dev/null +++ b/app/interactors/domain/force_delete/prepare_domain.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDelete + class PrepareDomain + include Interactor + + def call + domain = context.domain + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= domain.class.STATUSES_TO_SET + domain.save(validate: false) + end + end + end +end diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete/set_status.rb new file mode 100644 index 000000000..7ce481623 --- /dev/null +++ b/app/interactors/domain/force_delete/set_status.rb @@ -0,0 +1,36 @@ +class Domain + module ForceDelete + class SetStatus + include Interactor + + def call + domain.force_delete_type = context.type + context.type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + private + + def domain + @domain |= context.domain + end + + def force_delete_fast_track + domain.force_delete_date = force_delete_fast_track_start_date + 1.day + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) + end + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index f3bf96975..c6f9b598f 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -7,6 +7,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength :contact_notification_sent_date, :template_name + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + scope :notification_not_sent, lambda { where("(force_delete_data->>'contact_notification_sent_date') is null") From 09389ea6620150df572dbeedcc493a431bda58e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 9 Nov 2020 16:24:25 +0200 Subject: [PATCH 081/202] Registrant confirms: Poll domain change application --- .../api/v1/registrant/confirms_controller.rb | 55 +++++++++++++++++++ config/routes.rb | 4 +- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/v1/registrant/confirms_controller.rb diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb new file mode 100644 index 000000000..cbc8c5413 --- /dev/null +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -0,0 +1,55 @@ +require 'serializers/registrant_api/domain' + +module Api + module V1 + module Registrant + class ConfirmsController < ::Api::V1::Registrant::BaseController + skip_before_action :authenticate, :set_paper_trail_whodunnit + before_action :set_domain, only: %i[index update] + before_action :verify_updateable, only: %i[index update] + + def index + render json: { + domain_name: @domain.name, + current_registrant: serialized_registrant(@domain.registrant), + new_registrant: serialized_registrant(@domain.pending_registrant) + } + end + + def update + end + + private + + def serialized_registrant(registrant) + { + name: registrant.try(:name), + ident: registrant.try(:ident), + country: registrant.try(:ident_country_code) + } + end + + def confirmation_params + params do |p| + p.require(:name) + p.require(:token) + end + end + + def set_domain + @domain = Domain.find_by(name: confirmation_params[:name]) + return if @domain + + render json: { error: 'Domain not found' }, status: :not_found + end + + def verify_updateable + return if @domain.registrant_update_confirmable?(confirmation_params[:token]) + + render json: { error: 'Application expired or not found' }, + status: :unauthorized + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index f58063fae..0b74a2b97 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,12 +56,12 @@ Rails.application.routes.draw do namespace :v1 do namespace :registrant do post 'auth/eid', to: 'auth#eid' - + get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:token', to: 'confirms#update', constraints: { name: /[^\/]+/ } resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] end resources :contacts, only: %i[index show update], param: :uuid - resources :companies, only: %i[index] end resources :auctions, only: %i[index show update], param: :uuid From 03754b542b917fb6f10209f40a20ee038b610f5b Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 13:50:38 +0500 Subject: [PATCH 082/202] Complete creation of interactors --- .../admin/domains/force_delete_controller.rb | 18 ++++++----- .../domain/force_delete/check_discarded.rb | 4 +-- .../domain/force_delete/notify_by_email.rb | 31 +++++++++++++++++++ .../domain/force_delete/notify_registrar.rb | 16 ++++++++++ .../domain/force_delete/prepare_domain.rb | 6 +++- .../domain/force_delete/set_status.rb | 5 ++- app/models/concerns/domain/force_delete.rb | 15 +++++---- 7 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 app/interactors/domain/force_delete/notify_by_email.rb create mode 100644 app/interactors/domain/force_delete/notify_registrar.rb diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index 6a111926f..ca5588964 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -5,19 +5,21 @@ module Admin authorize! :manage, domain domain.transaction do - domain.schedule_force_delete(type: force_delete_type) - domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - - notify_by_email if notify_by_email? + # domain.schedule_force_delete(type: force_delete_type) + # domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', + # domain_name: domain.name, + # outzone_date: domain.outzone_date, + # purge_date: domain.purge_date)) # added to interactor + # + # notify_by_email if notify_by_email? # added to interactor + Domain::ForceDelete::Base.call(domain: domain, type: force_delete_type) end redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') end def notify_by_email + # added to interactor if force_delete_type == :fast_track send_email domain.update(contact_notification_sent_date: Time.zone.today) @@ -39,10 +41,12 @@ module Admin end def notify_by_email? + # added to interactor ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) end def send_email + # added to interactor DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, registrant: domain.registrant, diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete/check_discarded.rb index a13c16778..ac2cd31b6 100644 --- a/app/interactors/domain/force_delete/check_discarded.rb +++ b/app/interactors/domain/force_delete/check_discarded.rb @@ -6,8 +6,8 @@ class Domain def call return unless context.domain.discarded? - raise StandardError, - 'Force delete procedure cannot be scheduled while a domain is discarded' + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + context.fail!( message: message ) end end end diff --git a/app/interactors/domain/force_delete/notify_by_email.rb b/app/interactors/domain/force_delete/notify_by_email.rb new file mode 100644 index 000000000..949dd5838 --- /dev/null +++ b/app/interactors/domain/force_delete/notify_by_email.rb @@ -0,0 +1,31 @@ +class Domain + module ForceDelete + class NotifyByEmail + include Interactor + + def call + return unless notify_by_email? + + if context.type == :fast_track + send_email + context.domain.update(contact_notification_sent_date: Time.zone.today) + else + context.domain.update(template_name: context.domain.notification_template) + end + end + + private + + def notify_by_email? + ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) + end + + def send_email + DomainDeleteMailer.forced(domain: context.domain, + registrar: context.domain.registrar, + registrant: context.domain.registrant, + template_name: context.domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactors/domain/force_delete/notify_registrar.rb b/app/interactors/domain/force_delete/notify_registrar.rb new file mode 100644 index 000000000..345f4d7eb --- /dev/null +++ b/app/interactors/domain/force_delete/notify_registrar.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDelete + class NotifyRegistrar + include Interactor + + def call + domain = context.domain + + domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete/prepare_domain.rb index 4b3b83064..3bc764dc8 100644 --- a/app/interactors/domain/force_delete/prepare_domain.rb +++ b/app/interactors/domain/force_delete/prepare_domain.rb @@ -3,10 +3,14 @@ class Domain class PrepareDomain include Interactor + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + def call domain = context.domain domain.statuses_before_force_delete = domain.statuses - domain.statuses |= domain.class.STATUSES_TO_SET + domain.statuses |= STATUSES_TO_SET domain.save(validate: false) end end diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete/set_status.rb index 7ce481623..2a6c49daa 100644 --- a/app/interactors/domain/force_delete/set_status.rb +++ b/app/interactors/domain/force_delete/set_status.rb @@ -16,7 +16,10 @@ class Domain end def force_delete_fast_track - domain.force_delete_date = force_delete_fast_track_start_date + 1.day + domain.force_delete_date = Time.zone.today + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + + 1.day domain.force_delete_start = Time.zone.today + 1.day end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index c6f9b598f..768b1d11b 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -7,10 +7,6 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength :contact_notification_sent_date, :template_name - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - scope :notification_not_sent, lambda { where("(force_delete_data->>'contact_notification_sent_date') is null") @@ -57,6 +53,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track) + # added to interactor if discarded? raise StandardError, 'Force delete procedure cannot be scheduled while a domain is discarded' end @@ -72,8 +69,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength preserve_current_statuses_for_force_delete add_force_delete_statuses add_force_delete_type(:fast) - self.force_delete_date = force_delete_fast_track_start_date + 1.day - self.force_delete_start = Time.zone.today + 1.day + self.force_delete_date = force_delete_fast_track_start_date + 1.day # added to interactor + self.force_delete_start = Time.zone.today + 1.day # added to interactor stop_all_pending_actions allow_deletion save(validate: false) @@ -120,12 +117,14 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def soft_delete_dates(years) + # added to interactor self.force_delete_start = valid_to - years.years self.force_delete_date = force_delete_start + Setting.expire_warning_period.days + Setting.redemption_grace_period.days end def stop_all_pending_actions + # added to interactor statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_TRANSFER) statuses.delete(DomainStatus::PENDING_RENEW) @@ -133,6 +132,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def preserve_current_statuses_for_force_delete + # added to interactor update(statuses_before_force_delete: statuses) end @@ -142,6 +142,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def add_force_delete_statuses + # added to interactor self.statuses |= [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED] @@ -155,11 +156,13 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def allow_deletion + # added to interactor statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) end def force_delete_fast_track_start_date + # added to interactor Time.zone.today + Setting.expire_warning_period.days + Setting.redemption_grace_period.days end end From f97dff60029b953a946ba05074416abfc1a1a721 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 14:06:37 +0500 Subject: [PATCH 083/202] Fix tests & interactor process --- .../admin/domains/force_delete_controller.rb | 28 +------ app/interactors/domain/force_delete/base.rb | 16 ---- .../domain/force_delete/notify_by_email.rb | 31 -------- .../domain/force_delete/notify_registrar.rb | 16 ---- .../domain/force_delete_interactor/base.rb | 16 ++++ .../check_discarded.rb | 4 +- .../notify_by_email.rb | 31 ++++++++ .../notify_registrar.rb | 16 ++++ .../post_set_process.rb | 2 +- .../prepare_domain.rb | 2 +- .../set_status.rb | 25 +++--- app/models/concerns/domain/force_delete.rb | 79 +------------------ test/models/domain/force_delete_test.rb | 7 +- test/models/domain_test.rb | 2 +- 14 files changed, 93 insertions(+), 182 deletions(-) delete mode 100644 app/interactors/domain/force_delete/base.rb delete mode 100644 app/interactors/domain/force_delete/notify_by_email.rb delete mode 100644 app/interactors/domain/force_delete/notify_registrar.rb create mode 100644 app/interactors/domain/force_delete_interactor/base.rb rename app/interactors/domain/{force_delete => force_delete_interactor}/check_discarded.rb (77%) create mode 100644 app/interactors/domain/force_delete_interactor/notify_by_email.rb create mode 100644 app/interactors/domain/force_delete_interactor/notify_registrar.rb rename app/interactors/domain/{force_delete => force_delete_interactor}/post_set_process.rb (95%) rename app/interactors/domain/{force_delete => force_delete_interactor}/prepare_domain.rb (93%) rename app/interactors/domain/{force_delete => force_delete_interactor}/set_status.rb (61%) diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index ca5588964..9f660ed71 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -5,29 +5,12 @@ module Admin authorize! :manage, domain domain.transaction do - # domain.schedule_force_delete(type: force_delete_type) - # domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - # domain_name: domain.name, - # outzone_date: domain.outzone_date, - # purge_date: domain.purge_date)) # added to interactor - # - # notify_by_email if notify_by_email? # added to interactor - Domain::ForceDelete::Base.call(domain: domain, type: force_delete_type) + domain.schedule_force_delete(type: force_delete_type, notify_by_email: notify_by_email?) end redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') end - def notify_by_email - # added to interactor - if force_delete_type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - def destroy authorize! :manage, domain domain.cancel_force_delete @@ -41,18 +24,9 @@ module Admin end def notify_by_email? - # added to interactor ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) end - def send_email - # added to interactor - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - def force_delete_type soft_delete? ? :soft : :fast_track end diff --git a/app/interactors/domain/force_delete/base.rb b/app/interactors/domain/force_delete/base.rb deleted file mode 100644 index 79de3cf2f..000000000 --- a/app/interactors/domain/force_delete/base.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDelete - class Base - include Interactor::Organizer - - # As per https://github.com/collectiveidea/interactor#organizers - - organize Domain::ForceDelete::CheckDiscarded, - Domain::ForceDelete::PrepareDomain, - Domain::ForceDelete::SetStatus, - Domain::ForceDelete::PostSetProcess, - Domain::ForceDelete::NotifyRegistrar, - Domain::ForceDelete::NotifyByEmail - end - end -end diff --git a/app/interactors/domain/force_delete/notify_by_email.rb b/app/interactors/domain/force_delete/notify_by_email.rb deleted file mode 100644 index 949dd5838..000000000 --- a/app/interactors/domain/force_delete/notify_by_email.rb +++ /dev/null @@ -1,31 +0,0 @@ -class Domain - module ForceDelete - class NotifyByEmail - include Interactor - - def call - return unless notify_by_email? - - if context.type == :fast_track - send_email - context.domain.update(contact_notification_sent_date: Time.zone.today) - else - context.domain.update(template_name: context.domain.notification_template) - end - end - - private - - def notify_by_email? - ActiveRecord::Type::Boolean.new.cast(params[:notify_by_email]) - end - - def send_email - DomainDeleteMailer.forced(domain: context.domain, - registrar: context.domain.registrar, - registrant: context.domain.registrant, - template_name: context.domain.notification_template).deliver_now - end - end - end -end diff --git a/app/interactors/domain/force_delete/notify_registrar.rb b/app/interactors/domain/force_delete/notify_registrar.rb deleted file mode 100644 index 345f4d7eb..000000000 --- a/app/interactors/domain/force_delete/notify_registrar.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDelete - class NotifyRegistrar - include Interactor - - def call - domain = context.domain - - domain.registrar.notifications.create!(text: t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end - end -end diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb new file mode 100644 index 000000000..d10edcaef --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/base.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class Base + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDeleteInteractor::CheckDiscarded, + Domain::ForceDeleteInteractor::PrepareDomain, + Domain::ForceDeleteInteractor::SetStatus, + Domain::ForceDeleteInteractor::PostSetProcess, + Domain::ForceDeleteInteractor::NotifyRegistrar, + Domain::ForceDeleteInteractor::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete/check_discarded.rb b/app/interactors/domain/force_delete_interactor/check_discarded.rb similarity index 77% rename from app/interactors/domain/force_delete/check_discarded.rb rename to app/interactors/domain/force_delete_interactor/check_discarded.rb index ac2cd31b6..c340f9f43 100644 --- a/app/interactors/domain/force_delete/check_discarded.rb +++ b/app/interactors/domain/force_delete_interactor/check_discarded.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class CheckDiscarded include Interactor @@ -7,7 +7,7 @@ class Domain return unless context.domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' - context.fail!( message: message ) + context.fail!(message: message) end end end diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactors/domain/force_delete_interactor/notify_by_email.rb new file mode 100644 index 000000000..91855b839 --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/notify_by_email.rb @@ -0,0 +1,31 @@ +class Domain + module ForceDeleteInteractor + class NotifyByEmail + include Interactor + + def call + return unless context.notify_by_email + + if context.type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: context.domain.notification_template) + end + end + + private + + def domain + @domain ||= context.domain + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactors/domain/force_delete_interactor/notify_registrar.rb new file mode 100644 index 000000000..bfe35d29a --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/notify_registrar.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class NotifyRegistrar + include Interactor + + def call + domain = context.domain + + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactors/domain/force_delete/post_set_process.rb b/app/interactors/domain/force_delete_interactor/post_set_process.rb similarity index 95% rename from app/interactors/domain/force_delete/post_set_process.rb rename to app/interactors/domain/force_delete_interactor/post_set_process.rb index a2fc531b2..78a039d43 100644 --- a/app/interactors/domain/force_delete/post_set_process.rb +++ b/app/interactors/domain/force_delete_interactor/post_set_process.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class PostSetProcess include Interactor diff --git a/app/interactors/domain/force_delete/prepare_domain.rb b/app/interactors/domain/force_delete_interactor/prepare_domain.rb similarity index 93% rename from app/interactors/domain/force_delete/prepare_domain.rb rename to app/interactors/domain/force_delete_interactor/prepare_domain.rb index 3bc764dc8..c214a6b88 100644 --- a/app/interactors/domain/force_delete/prepare_domain.rb +++ b/app/interactors/domain/force_delete_interactor/prepare_domain.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class PrepareDomain include Interactor diff --git a/app/interactors/domain/force_delete/set_status.rb b/app/interactors/domain/force_delete_interactor/set_status.rb similarity index 61% rename from app/interactors/domain/force_delete/set_status.rb rename to app/interactors/domain/force_delete_interactor/set_status.rb index 2a6c49daa..a15b67ce2 100644 --- a/app/interactors/domain/force_delete/set_status.rb +++ b/app/interactors/domain/force_delete_interactor/set_status.rb @@ -1,5 +1,5 @@ class Domain - module ForceDelete + module ForceDeleteInteractor class SetStatus include Interactor @@ -9,23 +9,20 @@ class Domain domain.save(validate: false) end - private - def domain - @domain |= context.domain + @domain ||= context.domain end def force_delete_fast_track domain.force_delete_date = Time.zone.today + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days + - 1.day + expire_warning_period_days + + redemption_grace_period_days domain.force_delete_start = Time.zone.today + 1.day end def force_delete_soft - years = (valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? end def soft_forcedelete_dates(years) @@ -34,6 +31,16 @@ class Domain Setting.expire_warning_period.days + Setting.redemption_grace_period.days end + + private + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end end end end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 768b1d11b..26dc62413 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -52,38 +52,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength force_delete_start + Setting.expire_warning_period.days <= valid_to end - def schedule_force_delete(type: :fast_track) - # added to interactor - if discarded? - raise StandardError, 'Force delete procedure cannot be scheduled while a domain is discarded' - end - - type == :fast_track ? force_delete_fast_track : force_delete_soft - end - - def add_force_delete_type(force_delete_type) - self.force_delete_type = force_delete_type - end - - def force_delete_fast_track - preserve_current_statuses_for_force_delete - add_force_delete_statuses - add_force_delete_type(:fast) - self.force_delete_date = force_delete_fast_track_start_date + 1.day # added to interactor - self.force_delete_start = Time.zone.today + 1.day # added to interactor - stop_all_pending_actions - allow_deletion - save(validate: false) - end - - def force_delete_soft - preserve_current_statuses_for_force_delete - add_force_delete_statuses - add_force_delete_type(:soft) - calculate_soft_delete_date - stop_all_pending_actions - allow_deletion - save(validate: false) + def schedule_force_delete(type: :fast_track, notify_by_email: false) + Domain::ForceDeleteInteractor::Base.call(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data @@ -111,58 +83,15 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength private - def calculate_soft_delete_date - years = (valid_to.to_date - Time.zone.today).to_i / 365 - soft_delete_dates(years) if years.positive? - end - - def soft_delete_dates(years) - # added to interactor - self.force_delete_start = valid_to - years.years - self.force_delete_date = force_delete_start + Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def stop_all_pending_actions - # added to interactor - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - end - - def preserve_current_statuses_for_force_delete - # added to interactor - update(statuses_before_force_delete: statuses) - end - def restore_statuses_before_force_delete self.statuses = statuses_before_force_delete self.statuses_before_force_delete = nil end - def add_force_delete_statuses - # added to interactor - self.statuses |= [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED] - end - def remove_force_delete_statuses statuses.delete(DomainStatus::FORCE_DELETE) statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) statuses.delete(DomainStatus::CLIENT_HOLD) end - - def allow_deletion - # added to interactor - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - end - - def force_delete_fast_track_start_date - # added to interactor - Time.zone.today + Setting.expire_warning_period.days + Setting.redemption_grace_period.days - end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index f0723c326..f5c7e7b06 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,9 +111,10 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - assert_raises StandardError do - @domain.schedule_force_delete(type: :fast_track) - end + context = Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + + assert_not context.success? + assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message end def test_cancels_force_delete diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 4a9240f57..bb4dd37cf 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - @domain.schedule_force_delete(type: :fast_track) + Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 3c7fa8846357c797d51fab1b2cfb6bd099877cdb Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 10 Nov 2020 15:06:32 +0500 Subject: [PATCH 084/202] Inherit all the interactors from base one --- .../domain/force_delete_interactor/base.rb | 13 +++++-------- .../force_delete_interactor/check_discarded.rb | 6 ++---- .../force_delete_interactor/notify_by_email.rb | 10 +--------- .../force_delete_interactor/notify_registrar.rb | 6 +----- .../force_delete_interactor/post_set_process.rb | 8 +++----- .../force_delete_interactor/prepare_domain.rb | 5 +---- .../force_delete_interactor/set_force_delete.rb | 16 ++++++++++++++++ .../domain/force_delete_interactor/set_status.rb | 8 +------- app/models/concerns/domain/force_delete.rb | 6 +++--- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 11 files changed, 35 insertions(+), 47 deletions(-) create mode 100644 app/interactors/domain/force_delete_interactor/set_force_delete.rb diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb index d10edcaef..6724d53e3 100644 --- a/app/interactors/domain/force_delete_interactor/base.rb +++ b/app/interactors/domain/force_delete_interactor/base.rb @@ -1,16 +1,13 @@ class Domain module ForceDeleteInteractor class Base - include Interactor::Organizer + include Interactor - # As per https://github.com/collectiveidea/interactor#organizers + private - organize Domain::ForceDeleteInteractor::CheckDiscarded, - Domain::ForceDeleteInteractor::PrepareDomain, - Domain::ForceDeleteInteractor::SetStatus, - Domain::ForceDeleteInteractor::PostSetProcess, - Domain::ForceDeleteInteractor::NotifyRegistrar, - Domain::ForceDeleteInteractor::NotifyByEmail + def domain + @domain ||= context.domain + end end end end diff --git a/app/interactors/domain/force_delete_interactor/check_discarded.rb b/app/interactors/domain/force_delete_interactor/check_discarded.rb index c340f9f43..b63828f49 100644 --- a/app/interactors/domain/force_delete_interactor/check_discarded.rb +++ b/app/interactors/domain/force_delete_interactor/check_discarded.rb @@ -1,10 +1,8 @@ class Domain module ForceDeleteInteractor - class CheckDiscarded - include Interactor - + class CheckDiscarded < Base def call - return unless context.domain.discarded? + return unless domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' context.fail!(message: message) diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactors/domain/force_delete_interactor/notify_by_email.rb index 91855b839..5263fdd7a 100644 --- a/app/interactors/domain/force_delete_interactor/notify_by_email.rb +++ b/app/interactors/domain/force_delete_interactor/notify_by_email.rb @@ -1,8 +1,6 @@ class Domain module ForceDeleteInteractor - class NotifyByEmail - include Interactor - + class NotifyByEmail < Base def call return unless context.notify_by_email @@ -14,12 +12,6 @@ class Domain end end - private - - def domain - @domain ||= context.domain - end - def send_email DomainDeleteMailer.forced(domain: domain, registrar: domain.registrar, diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactors/domain/force_delete_interactor/notify_registrar.rb index bfe35d29a..bd891d5dc 100644 --- a/app/interactors/domain/force_delete_interactor/notify_registrar.rb +++ b/app/interactors/domain/force_delete_interactor/notify_registrar.rb @@ -1,11 +1,7 @@ class Domain module ForceDeleteInteractor - class NotifyRegistrar - include Interactor - + class NotifyRegistrar < Base def call - domain = context.domain - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', domain_name: domain.name, outzone_date: domain.outzone_date, diff --git a/app/interactors/domain/force_delete_interactor/post_set_process.rb b/app/interactors/domain/force_delete_interactor/post_set_process.rb index 78a039d43..4017eb459 100644 --- a/app/interactors/domain/force_delete_interactor/post_set_process.rb +++ b/app/interactors/domain/force_delete_interactor/post_set_process.rb @@ -1,10 +1,8 @@ class Domain module ForceDeleteInteractor - class PostSetProcess - include Interactor - + class PostSetProcess < Base def call - statuses = context.domain.statuses + statuses = domain.statuses # Stop all pending actions statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_TRANSFER) @@ -14,7 +12,7 @@ class Domain # Allow deletion statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - context.domain.save(validate: false) + domain.save(validate: false) end end end diff --git a/app/interactors/domain/force_delete_interactor/prepare_domain.rb b/app/interactors/domain/force_delete_interactor/prepare_domain.rb index c214a6b88..6317bbaf1 100644 --- a/app/interactors/domain/force_delete_interactor/prepare_domain.rb +++ b/app/interactors/domain/force_delete_interactor/prepare_domain.rb @@ -1,14 +1,11 @@ class Domain module ForceDeleteInteractor - class PrepareDomain - include Interactor - + class PrepareDomain < Base STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze def call - domain = context.domain domain.statuses_before_force_delete = domain.statuses domain.statuses |= STATUSES_TO_SET domain.save(validate: false) diff --git a/app/interactors/domain/force_delete_interactor/set_force_delete.rb b/app/interactors/domain/force_delete_interactor/set_force_delete.rb new file mode 100644 index 000000000..4435b8f8c --- /dev/null +++ b/app/interactors/domain/force_delete_interactor/set_force_delete.rb @@ -0,0 +1,16 @@ +class Domain + module ForceDeleteInteractor + class SetForceDelete + include Interactor::Organizer + + # As per https://github.com/collectiveidea/interactor#organizers + + organize Domain::ForceDeleteInteractor::CheckDiscarded, + Domain::ForceDeleteInteractor::PrepareDomain, + Domain::ForceDeleteInteractor::SetStatus, + Domain::ForceDeleteInteractor::PostSetProcess, + Domain::ForceDeleteInteractor::NotifyRegistrar, + Domain::ForceDeleteInteractor::NotifyByEmail + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/set_status.rb b/app/interactors/domain/force_delete_interactor/set_status.rb index a15b67ce2..418952f51 100644 --- a/app/interactors/domain/force_delete_interactor/set_status.rb +++ b/app/interactors/domain/force_delete_interactor/set_status.rb @@ -1,18 +1,12 @@ class Domain module ForceDeleteInteractor - class SetStatus - include Interactor - + class SetStatus < Base def call domain.force_delete_type = context.type context.type == :fast_track ? force_delete_fast_track : force_delete_soft domain.save(validate: false) end - def domain - @domain ||= context.domain - end - def force_delete_fast_track domain.force_delete_date = Time.zone.today + expire_warning_period_days + diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 26dc62413..4a032ab36 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,9 +53,9 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteractor::Base.call(domain: self, - type: type, - notify_by_email: notify_by_email) + Domain::ForceDeleteInteractor::SetForceDelete.call(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index f5c7e7b06..ddd330f47 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,7 +111,7 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - context = Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + context = Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) assert_not context.success? assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index bb4dd37cf..2b01c795f 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteractor::Base.call(domain: @domain, type: :fast_track) + Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 377a95cc76713b40fd9b2890811bdcb2190a39db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 13:09:32 +0200 Subject: [PATCH 085/202] Add experimental registrant change accept/deny API endpoint --- .../api/v1/registrant/confirms_controller.rb | 13 +++++++++++++ app/services/registrant_change.rb | 1 + 2 files changed, 14 insertions(+) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index cbc8c5413..390753fd3 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -17,10 +17,23 @@ module Api end def update + verification = RegistrantVerification.new(domain_id: @domain.id, + verification_token: confirmation_params[:token]) + + head(update_action(verification) ? :ok : :bad_request) end private + def update_action(verification) + initiator = "email link, #{t(:user_not_authenticated)}" + if params[:confirm].present? + verification.domain_registrant_change_confirm!(initiator) + else + verification.domain_registrant_change_reject!(initiator) + end + end + def serialized_registrant(registrant) { name: registrant.try(:name), diff --git a/app/services/registrant_change.rb b/app/services/registrant_change.rb index 35b631fb6..fdee7654a 100644 --- a/app/services/registrant_change.rb +++ b/app/services/registrant_change.rb @@ -5,6 +5,7 @@ class RegistrantChange end def confirm + Dispute.close_by_domain(@domain.name) if @domain.disputed? notify_registrant end From 55e66724cf2ae6be3394a0074b469d4adc8578e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:21:32 +0200 Subject: [PATCH 086/202] Clean up verifications controller --- .../api/v1/registrant/confirms_controller.rb | 39 ++++++++++++++----- config/routes.rb | 3 +- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 390753fd3..7d3dd5552 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -7,6 +7,7 @@ module Api skip_before_action :authenticate, :set_paper_trail_whodunnit before_action :set_domain, only: %i[index update] before_action :verify_updateable, only: %i[index update] + before_action :verify_decision, only: %i[update] def index render json: { @@ -18,16 +19,30 @@ module Api def update verification = RegistrantVerification.new(domain_id: @domain.id, - verification_token: confirmation_params[:token]) + verification_token: verify_params[:token]) - head(update_action(verification) ? :ok : :bad_request) + head(:bad_request) and return unless update_action(verification) + + render json: { + domain_name: @domain.name, + current_registrant: serialized_registrant(current_registrant), + status: params[:decision] + } end private + def current_registrant + changes_registrant? ? @domain.registrant : @domain.pending_registrant + end + + def changes_registrant? + params[:decision] == 'confirmed' + end + def update_action(verification) - initiator = "email link, #{t(:user_not_authenticated)}" - if params[:confirm].present? + initiator = "email link, #{I18n.t(:user_not_authenticated)}" + if changes_registrant? verification.domain_registrant_change_confirm!(initiator) else verification.domain_registrant_change_reject!(initiator) @@ -42,25 +57,31 @@ module Api } end - def confirmation_params + def verify_params params do |p| p.require(:name) p.require(:token) end end + def verify_decision + return if %w[confirmed rejected].include?(params[:decision]) + + head :bad_request + end + def set_domain - @domain = Domain.find_by(name: confirmation_params[:name]) + @domain = Domain.find_by(name: verify_params[:name]) + @domain ||= Domain.find_by(name_puny: verify_params[:name]) return if @domain render json: { error: 'Domain not found' }, status: :not_found end def verify_updateable - return if @domain.registrant_update_confirmable?(confirmation_params[:token]) + return if @domain.registrant_update_confirmable?(verify_params[:token]) - render json: { error: 'Application expired or not found' }, - status: :unauthorized + render json: { error: 'Application expired or not found' }, status: :unauthorized end end end diff --git a/config/routes.rb b/config/routes.rb index 0b74a2b97..107484f6e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,7 +57,8 @@ Rails.application.routes.draw do namespace :registrant do post 'auth/eid', to: 'auth#eid' get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } - post 'confirms/:name/:token', to: 'confirms#update', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } + resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] end From 6e1a836c97e72a3fd0183835178362766246903d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:23:01 +0200 Subject: [PATCH 087/202] Fix CC issues --- app/controllers/api/v1/registrant/confirms_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 7d3dd5552..712df6bc1 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -13,7 +13,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(@domain.registrant), - new_registrant: serialized_registrant(@domain.pending_registrant) + new_registrant: serialized_registrant(@domain.pending_registrant), } end @@ -26,7 +26,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(current_registrant), - status: params[:decision] + status: params[:decision], } end @@ -53,7 +53,7 @@ module Api { name: registrant.try(:name), ident: registrant.try(:ident), - country: registrant.try(:ident_country_code) + country: registrant.try(:ident_country_code), } end From bce39e3404535535ce2097f2b46069e5872b092e Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 11 Nov 2020 15:44:00 +0500 Subject: [PATCH 088/202] Move interactor from Interactor gem to ActiveInteraction --- Gemfile | 3 +-- Gemfile.lock | 9 +++------ .../admin/domains/force_delete_controller.rb | 8 ++++++-- .../domain/force_delete_interaction/base.rb | 17 +++++++++++++++++ .../check_discarded.rb | 8 ++++---- .../notify_by_email.rb | 10 +++++----- .../notify_registrar.rb | 4 ++-- .../post_set_process.rb | 4 ++-- .../force_delete_interaction}/prepare_domain.rb | 4 ++-- .../set_force_delete.rb | 14 ++++++++++++++ .../force_delete_interaction}/set_status.rb | 12 ++++++------ .../domain/force_delete_interactor/base.rb | 13 ------------- .../force_delete_interactor/set_force_delete.rb | 16 ---------------- app/models/concerns/domain/force_delete.rb | 2 +- config/application.rb | 1 + test/models/domain/force_delete_test.rb | 8 +++++--- test/models/domain_test.rb | 2 +- 17 files changed, 70 insertions(+), 65 deletions(-) create mode 100644 app/interactions/domain/force_delete_interaction/base.rb rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/check_discarded.rb (55%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/notify_by_email.rb (70%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/notify_registrar.rb (89%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/post_set_process.rb (91%) rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/prepare_domain.rb (89%) create mode 100644 app/interactions/domain/force_delete_interaction/set_force_delete.rb rename app/{interactors/domain/force_delete_interactor => interactions/domain/force_delete_interaction}/set_status.rb (86%) delete mode 100644 app/interactors/domain/force_delete_interactor/base.rb delete mode 100644 app/interactors/domain/force_delete_interactor/set_force_delete.rb diff --git a/Gemfile b/Gemfile index c4f5a7cf8..d35238fc0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,8 @@ source 'https://rubygems.org' # core +gem 'active_interaction', '~> 3.8' gem 'bootsnap', '>= 1.1.0', require: false -gem 'interactor', '~> 3.0' -gem 'interactor-rails', '~> 2.0' gem 'iso8601', '0.12.1' # for dates and times gem 'rails', '~> 6.0' gem 'rest-client' diff --git a/Gemfile.lock b/Gemfile.lock index cc59a8f41..14970c2c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -112,6 +112,8 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_interaction (3.8.3) + activemodel (>= 4, < 7) activejob (6.0.3.3) activesupport (= 6.0.3.3) globalid (>= 0.3.6) @@ -251,10 +253,6 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) i18n_data (0.10.0) - interactor (3.1.2) - interactor-rails (2.2.1) - interactor (~> 3.0) - rails (>= 4.2) isikukood (0.1.2) iso8601 (0.12.1) jquery-rails (4.4.0) @@ -525,6 +523,7 @@ PLATFORMS ruby DEPENDENCIES + active_interaction (~> 3.8) activerecord-import airbrake bootsnap (>= 1.1.0) @@ -548,8 +547,6 @@ DEPENDENCIES figaro (= 1.1.1) grape haml (~> 5.0) - interactor (~> 3.0) - interactor-rails (~> 2.0) isikukood iso8601 (= 0.12.1) jquery-rails diff --git a/app/controllers/admin/domains/force_delete_controller.rb b/app/controllers/admin/domains/force_delete_controller.rb index 9f660ed71..4fe85fa3b 100644 --- a/app/controllers/admin/domains/force_delete_controller.rb +++ b/app/controllers/admin/domains/force_delete_controller.rb @@ -4,11 +4,15 @@ module Admin def create authorize! :manage, domain + notice = t('.scheduled') + domain.transaction do - domain.schedule_force_delete(type: force_delete_type, notify_by_email: notify_by_email?) + result = domain.schedule_force_delete(type: force_delete_type, + notify_by_email: notify_by_email?) + notice = result.errors.messages[:domain].first unless result.valid? end - redirect_to edit_admin_domain_url(domain), notice: t('.scheduled') + redirect_to edit_admin_domain_url(domain), notice: notice end def destroy diff --git a/app/interactions/domain/force_delete_interaction/base.rb b/app/interactions/domain/force_delete_interaction/base.rb new file mode 100644 index 000000000..e8079982b --- /dev/null +++ b/app/interactions/domain/force_delete_interaction/base.rb @@ -0,0 +1,17 @@ +class Domain + module ForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/check_discarded.rb b/app/interactions/domain/force_delete_interaction/check_discarded.rb similarity index 55% rename from app/interactors/domain/force_delete_interactor/check_discarded.rb rename to app/interactions/domain/force_delete_interaction/check_discarded.rb index b63828f49..d2b54641a 100644 --- a/app/interactors/domain/force_delete_interactor/check_discarded.rb +++ b/app/interactions/domain/force_delete_interaction/check_discarded.rb @@ -1,11 +1,11 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class CheckDiscarded < Base - def call - return unless domain.discarded? + def execute + return true unless domain.discarded? message = 'Force delete procedure cannot be scheduled while a domain is discarded' - context.fail!(message: message) + errors.add(:domain, message) end end end diff --git a/app/interactors/domain/force_delete_interactor/notify_by_email.rb b/app/interactions/domain/force_delete_interaction/notify_by_email.rb similarity index 70% rename from app/interactors/domain/force_delete_interactor/notify_by_email.rb rename to app/interactions/domain/force_delete_interaction/notify_by_email.rb index 5263fdd7a..97abc8e5c 100644 --- a/app/interactors/domain/force_delete_interactor/notify_by_email.rb +++ b/app/interactions/domain/force_delete_interaction/notify_by_email.rb @@ -1,14 +1,14 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class NotifyByEmail < Base - def call - return unless context.notify_by_email + def execute + return unless notify_by_email - if context.type == :fast_track + if type == :fast_track send_email domain.update(contact_notification_sent_date: Time.zone.today) else - domain.update(template_name: context.domain.notification_template) + domain.update(template_name: domain.notification_template) end end diff --git a/app/interactors/domain/force_delete_interactor/notify_registrar.rb b/app/interactions/domain/force_delete_interaction/notify_registrar.rb similarity index 89% rename from app/interactors/domain/force_delete_interactor/notify_registrar.rb rename to app/interactions/domain/force_delete_interaction/notify_registrar.rb index bd891d5dc..da3e400cc 100644 --- a/app/interactors/domain/force_delete_interactor/notify_registrar.rb +++ b/app/interactions/domain/force_delete_interaction/notify_registrar.rb @@ -1,7 +1,7 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class NotifyRegistrar < Base - def call + def execute domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', domain_name: domain.name, outzone_date: domain.outzone_date, diff --git a/app/interactors/domain/force_delete_interactor/post_set_process.rb b/app/interactions/domain/force_delete_interaction/post_set_process.rb similarity index 91% rename from app/interactors/domain/force_delete_interactor/post_set_process.rb rename to app/interactions/domain/force_delete_interaction/post_set_process.rb index 4017eb459..68eb59bf9 100644 --- a/app/interactors/domain/force_delete_interactor/post_set_process.rb +++ b/app/interactions/domain/force_delete_interaction/post_set_process.rb @@ -1,7 +1,7 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class PostSetProcess < Base - def call + def execute statuses = domain.statuses # Stop all pending actions statuses.delete(DomainStatus::PENDING_UPDATE) diff --git a/app/interactors/domain/force_delete_interactor/prepare_domain.rb b/app/interactions/domain/force_delete_interaction/prepare_domain.rb similarity index 89% rename from app/interactors/domain/force_delete_interactor/prepare_domain.rb rename to app/interactions/domain/force_delete_interaction/prepare_domain.rb index 6317bbaf1..fddfeb75a 100644 --- a/app/interactors/domain/force_delete_interactor/prepare_domain.rb +++ b/app/interactions/domain/force_delete_interaction/prepare_domain.rb @@ -1,11 +1,11 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class PrepareDomain < Base STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, DomainStatus::SERVER_RENEW_PROHIBITED, DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - def call + def execute domain.statuses_before_force_delete = domain.statuses domain.statuses |= STATUSES_TO_SET domain.save(validate: false) diff --git a/app/interactions/domain/force_delete_interaction/set_force_delete.rb b/app/interactions/domain/force_delete_interaction/set_force_delete.rb new file mode 100644 index 000000000..6bbd8ef20 --- /dev/null +++ b/app/interactions/domain/force_delete_interaction/set_force_delete.rb @@ -0,0 +1,14 @@ +class Domain + module ForceDeleteInteraction + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end + end +end diff --git a/app/interactors/domain/force_delete_interactor/set_status.rb b/app/interactions/domain/force_delete_interaction/set_status.rb similarity index 86% rename from app/interactors/domain/force_delete_interactor/set_status.rb rename to app/interactions/domain/force_delete_interaction/set_status.rb index 418952f51..a56069fcc 100644 --- a/app/interactors/domain/force_delete_interactor/set_status.rb +++ b/app/interactions/domain/force_delete_interaction/set_status.rb @@ -1,9 +1,9 @@ class Domain - module ForceDeleteInteractor + module ForceDeleteInteraction class SetStatus < Base - def call - domain.force_delete_type = context.type - context.type == :fast_track ? force_delete_fast_track : force_delete_soft + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft domain.save(validate: false) end @@ -19,6 +19,8 @@ class Domain soft_forcedelete_dates(years) if years.positive? end + private + def soft_forcedelete_dates(years) domain.force_delete_start = domain.valid_to - years.years domain.force_delete_date = domain.force_delete_start + @@ -26,8 +28,6 @@ class Domain Setting.redemption_grace_period.days end - private - def redemption_grace_period_days Setting.redemption_grace_period.days + 1.day end diff --git a/app/interactors/domain/force_delete_interactor/base.rb b/app/interactors/domain/force_delete_interactor/base.rb deleted file mode 100644 index 6724d53e3..000000000 --- a/app/interactors/domain/force_delete_interactor/base.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Domain - module ForceDeleteInteractor - class Base - include Interactor - - private - - def domain - @domain ||= context.domain - end - end - end -end diff --git a/app/interactors/domain/force_delete_interactor/set_force_delete.rb b/app/interactors/domain/force_delete_interactor/set_force_delete.rb deleted file mode 100644 index 4435b8f8c..000000000 --- a/app/interactors/domain/force_delete_interactor/set_force_delete.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Domain - module ForceDeleteInteractor - class SetForceDelete - include Interactor::Organizer - - # As per https://github.com/collectiveidea/interactor#organizers - - organize Domain::ForceDeleteInteractor::CheckDiscarded, - Domain::ForceDeleteInteractor::PrepareDomain, - Domain::ForceDeleteInteractor::SetStatus, - Domain::ForceDeleteInteractor::PostSetProcess, - Domain::ForceDeleteInteractor::NotifyRegistrar, - Domain::ForceDeleteInteractor::NotifyByEmail - end - end -end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 4a032ab36..f81669c74 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,7 +53,7 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteractor::SetForceDelete.call(domain: self, + Domain::ForceDeleteInteraction::SetForceDelete.run(domain: self, type: type, notify_by_email: notify_by_email) end diff --git a/config/application.rb b/config/application.rb index 5f4481512..a5fb17c9d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -36,6 +36,7 @@ module DomainNameRegistry # Autoload all model subdirs config.autoload_paths += Dir[Rails.root.join('app', 'models', '**/')] + config.autoload_paths += Dir[Rails.root.join('app', 'interactions', '**/')] config.eager_load_paths << config.root.join('lib', 'validators') config.watchable_dirs['lib'] = %i[rb] diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index ddd330f47..b57763342 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,10 +111,12 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - context = Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) + result = Domain::ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) - assert_not context.success? - assert_equal 'Force delete procedure cannot be scheduled while a domain is discarded', context.message + assert_not result.valid? + assert_not @domain.force_delete_scheduled? + message = ["Force delete procedure cannot be scheduled while a domain is discarded"] + assert_equal message, result.errors.messages[:domain] end def test_cancels_force_delete diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 2b01c795f..514efaf14 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteractor::SetForceDelete.call(domain: @domain, type: :fast_track) + Domain::ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From e62f0a077abda210a76866e1184d0e420391aa34 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 11 Nov 2020 21:16:27 +0500 Subject: [PATCH 089/202] Remove domain namespace --- .../domain/force_delete_interaction/base.rb | 17 -------- .../check_discarded.rb | 12 ------ .../notify_by_email.rb | 23 ----------- .../notify_registrar.rb | 12 ------ .../post_set_process.rb | 19 --------- .../prepare_domain.rb | 15 ------- .../set_force_delete.rb | 14 ------- .../force_delete_interaction/set_status.rb | 40 ------------------- .../force_delete_interaction/base.rb | 16 ++++++++ .../check_discarded.rb | 11 +++++ .../notify_by_email.rb | 21 ++++++++++ .../notify_registrar.rb | 10 +++++ .../post_set_process.rb | 17 ++++++++ .../prepare_domain.rb | 13 ++++++ .../set_force_delete.rb | 12 ++++++ .../force_delete_interaction/set_status.rb | 38 ++++++++++++++++++ app/models/concerns/domain/force_delete.rb | 6 +-- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 19 files changed, 143 insertions(+), 157 deletions(-) delete mode 100644 app/interactions/domain/force_delete_interaction/base.rb delete mode 100644 app/interactions/domain/force_delete_interaction/check_discarded.rb delete mode 100644 app/interactions/domain/force_delete_interaction/notify_by_email.rb delete mode 100644 app/interactions/domain/force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/domain/force_delete_interaction/post_set_process.rb delete mode 100644 app/interactions/domain/force_delete_interaction/prepare_domain.rb delete mode 100644 app/interactions/domain/force_delete_interaction/set_force_delete.rb delete mode 100644 app/interactions/domain/force_delete_interaction/set_status.rb create mode 100644 app/interactions/force_delete_interaction/base.rb create mode 100644 app/interactions/force_delete_interaction/check_discarded.rb create mode 100644 app/interactions/force_delete_interaction/notify_by_email.rb create mode 100644 app/interactions/force_delete_interaction/notify_registrar.rb create mode 100644 app/interactions/force_delete_interaction/post_set_process.rb create mode 100644 app/interactions/force_delete_interaction/prepare_domain.rb create mode 100644 app/interactions/force_delete_interaction/set_force_delete.rb create mode 100644 app/interactions/force_delete_interaction/set_status.rb diff --git a/app/interactions/domain/force_delete_interaction/base.rb b/app/interactions/domain/force_delete_interaction/base.rb deleted file mode 100644 index e8079982b..000000000 --- a/app/interactions/domain/force_delete_interaction/base.rb +++ /dev/null @@ -1,17 +0,0 @@ -class Domain - module ForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to set ForceDelete on' - symbol :type, - default: :fast_track, - description: 'Force delete type, might be :fast_track or :soft' - boolean :notify_by_email, - default: false, - description: 'Do we need to send email notification' - - validates :type, inclusion: { in: %i[fast_track soft] } - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/check_discarded.rb b/app/interactions/domain/force_delete_interaction/check_discarded.rb deleted file mode 100644 index d2b54641a..000000000 --- a/app/interactions/domain/force_delete_interaction/check_discarded.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Domain - module ForceDeleteInteraction - class CheckDiscarded < Base - def execute - return true unless domain.discarded? - - message = 'Force delete procedure cannot be scheduled while a domain is discarded' - errors.add(:domain, message) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/notify_by_email.rb b/app/interactions/domain/force_delete_interaction/notify_by_email.rb deleted file mode 100644 index 97abc8e5c..000000000 --- a/app/interactions/domain/force_delete_interaction/notify_by_email.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Domain - module ForceDeleteInteraction - class NotifyByEmail < Base - def execute - return unless notify_by_email - - if type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - - def send_email - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/notify_registrar.rb b/app/interactions/domain/force_delete_interaction/notify_registrar.rb deleted file mode 100644 index da3e400cc..000000000 --- a/app/interactions/domain/force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Domain - module ForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/post_set_process.rb b/app/interactions/domain/force_delete_interaction/post_set_process.rb deleted file mode 100644 index 68eb59bf9..000000000 --- a/app/interactions/domain/force_delete_interaction/post_set_process.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Domain - module ForceDeleteInteraction - class PostSetProcess < Base - def execute - statuses = domain.statuses - # Stop all pending actions - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - - # Allow deletion - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - domain.save(validate: false) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/prepare_domain.rb b/app/interactions/domain/force_delete_interaction/prepare_domain.rb deleted file mode 100644 index fddfeb75a..000000000 --- a/app/interactions/domain/force_delete_interaction/prepare_domain.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Domain - module ForceDeleteInteraction - class PrepareDomain < Base - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - - def execute - domain.statuses_before_force_delete = domain.statuses - domain.statuses |= STATUSES_TO_SET - domain.save(validate: false) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/set_force_delete.rb b/app/interactions/domain/force_delete_interaction/set_force_delete.rb deleted file mode 100644 index 6bbd8ef20..000000000 --- a/app/interactions/domain/force_delete_interaction/set_force_delete.rb +++ /dev/null @@ -1,14 +0,0 @@ -class Domain - module ForceDeleteInteraction - class SetForceDelete < Base - def execute - compose(CheckDiscarded, inputs) - compose(PrepareDomain, inputs) - compose(SetStatus, inputs) - compose(PostSetProcess, inputs) - compose(NotifyRegistrar, inputs) - compose(NotifyByEmail, inputs) - end - end - end -end diff --git a/app/interactions/domain/force_delete_interaction/set_status.rb b/app/interactions/domain/force_delete_interaction/set_status.rb deleted file mode 100644 index a56069fcc..000000000 --- a/app/interactions/domain/force_delete_interaction/set_status.rb +++ /dev/null @@ -1,40 +0,0 @@ -class Domain - module ForceDeleteInteraction - class SetStatus < Base - def execute - domain.force_delete_type = type - type == :fast_track ? force_delete_fast_track : force_delete_soft - domain.save(validate: false) - end - - def force_delete_fast_track - domain.force_delete_date = Time.zone.today + - expire_warning_period_days + - redemption_grace_period_days - domain.force_delete_start = Time.zone.today + 1.day - end - - def force_delete_soft - years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) if years.positive? - end - - private - - def soft_forcedelete_dates(years) - domain.force_delete_start = domain.valid_to - years.years - domain.force_delete_date = domain.force_delete_start + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def redemption_grace_period_days - Setting.redemption_grace_period.days + 1.day - end - - def expire_warning_period_days - Setting.expire_warning_period.days - end - end - end -end diff --git a/app/interactions/force_delete_interaction/base.rb b/app/interactions/force_delete_interaction/base.rb new file mode 100644 index 000000000..4764ce8fe --- /dev/null +++ b/app/interactions/force_delete_interaction/base.rb @@ -0,0 +1,16 @@ +module ForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end +end + diff --git a/app/interactions/force_delete_interaction/check_discarded.rb b/app/interactions/force_delete_interaction/check_discarded.rb new file mode 100644 index 000000000..e0c893294 --- /dev/null +++ b/app/interactions/force_delete_interaction/check_discarded.rb @@ -0,0 +1,11 @@ +module ForceDeleteInteraction + class CheckDiscarded < Base + def execute + return true unless domain.discarded? + + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + errors.add(:domain, message) + end + end +end + diff --git a/app/interactions/force_delete_interaction/notify_by_email.rb b/app/interactions/force_delete_interaction/notify_by_email.rb new file mode 100644 index 000000000..541df258d --- /dev/null +++ b/app/interactions/force_delete_interaction/notify_by_email.rb @@ -0,0 +1,21 @@ +module ForceDeleteInteraction + class NotifyByEmail < Base + def execute + return unless notify_by_email + + if type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: domain.notification_template) + end + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end +end diff --git a/app/interactions/force_delete_interaction/notify_registrar.rb b/app/interactions/force_delete_interaction/notify_registrar.rb new file mode 100644 index 000000000..691e54f7e --- /dev/null +++ b/app/interactions/force_delete_interaction/notify_registrar.rb @@ -0,0 +1,10 @@ +module ForceDeleteInteraction + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end +end diff --git a/app/interactions/force_delete_interaction/post_set_process.rb b/app/interactions/force_delete_interaction/post_set_process.rb new file mode 100644 index 000000000..e51acf9b7 --- /dev/null +++ b/app/interactions/force_delete_interaction/post_set_process.rb @@ -0,0 +1,17 @@ +module ForceDeleteInteraction + class PostSetProcess < Base + def execute + statuses = domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + domain.save(validate: false) + end + end +end diff --git a/app/interactions/force_delete_interaction/prepare_domain.rb b/app/interactions/force_delete_interaction/prepare_domain.rb new file mode 100644 index 000000000..97f364145 --- /dev/null +++ b/app/interactions/force_delete_interaction/prepare_domain.rb @@ -0,0 +1,13 @@ +module ForceDeleteInteraction + class PrepareDomain < Base + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + + def execute + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= STATUSES_TO_SET + domain.save(validate: false) + end + end +end diff --git a/app/interactions/force_delete_interaction/set_force_delete.rb b/app/interactions/force_delete_interaction/set_force_delete.rb new file mode 100644 index 000000000..4608b5127 --- /dev/null +++ b/app/interactions/force_delete_interaction/set_force_delete.rb @@ -0,0 +1,12 @@ +module ForceDeleteInteraction + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end +end diff --git a/app/interactions/force_delete_interaction/set_status.rb b/app/interactions/force_delete_interaction/set_status.rb new file mode 100644 index 000000000..7d104aeee --- /dev/null +++ b/app/interactions/force_delete_interaction/set_status.rb @@ -0,0 +1,38 @@ +module ForceDeleteInteraction + class SetStatus < Base + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + def force_delete_fast_track + domain.force_delete_date = Time.zone.today + + expire_warning_period_days + + redemption_grace_period_days + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? + end + + private + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index f81669c74..b119b0ce0 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,9 +53,9 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domain::ForceDeleteInteraction::SetForceDelete.run(domain: self, - type: type, - notify_by_email: notify_by_email) + ForceDeleteInteraction::SetForceDelete.run(domain: self, + type: type, + notify_by_email: notify_by_email) end def clear_force_delete_data diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index b57763342..2c264d916 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -111,7 +111,7 @@ class NewDomainForceDeleteTest < ActiveSupport::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - result = Domain::ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) + result = ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) assert_not result.valid? assert_not @domain.force_delete_scheduled? diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 514efaf14..cc88cf35f 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - Domain::ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) + ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 4eaa8065ba3efbb5cb6890ef8215ef3c4b055b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 11 Nov 2020 16:49:26 +0200 Subject: [PATCH 090/202] Mailer: Enable registrant confirm actions via REST --- app/mailers/domain_delete_mailer.rb | 7 ++++++- config/application.yml.sample | 3 +++ config/routes.rb | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index 1f08204bf..c4190fe14 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -53,7 +53,12 @@ class DomainDeleteMailer < ApplicationMailer private def confirmation_url(domain) - registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) + base_url = ENV['registrant_portal_verifications_base_url'] + if base_url.blank? + registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) + else + "#{base_url}/confirmation/#{domain.name_puny}/#{domain.registrant_verification_token}" + end end def forced_email_from diff --git a/config/application.yml.sample b/config/application.yml.sample index ab64ed35e..acaa536dd 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -87,6 +87,9 @@ sk_digi_doc_service_name: 'Testimine' registrant_api_base_url: registrant_api_auth_allowed_ips: '127.0.0.1, 0.0.0.0' #ips, separated with commas +# Base URL (inc. https://) of REST registrant portal +# Leave blank to use internal registrant portal +registrant_portal_verifications_base_url: '' # # MISC diff --git a/config/routes.rb b/config/routes.rb index 107484f6e..7061f125f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,6 +63,7 @@ Rails.application.routes.draw do resource :registry_lock, only: %i[create destroy] end resources :contacts, only: %i[index show update], param: :uuid + resources :companies, only: %i[index] end resources :auctions, only: %i[index show update], param: :uuid From 64d35a864f840ea4db72f5e9af047c1b4a4b3fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 11:19:16 +0200 Subject: [PATCH 091/202] Add delete action to confirmations API endpoint --- .../api/v1/registrant/confirms_controller.rb | 49 +++++++++++++------ app/mailers/domain_delete_mailer.rb | 2 +- app/mailers/registrant_change_mailer.rb | 7 ++- config/routes.rb | 4 +- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index 712df6bc1..b04a72449 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -6,7 +6,7 @@ module Api class ConfirmsController < ::Api::V1::Registrant::BaseController skip_before_action :authenticate, :set_paper_trail_whodunnit before_action :set_domain, only: %i[index update] - before_action :verify_updateable, only: %i[index update] + before_action :verify_action, only: %i[index update] before_action :verify_decision, only: %i[update] def index @@ -21,7 +21,10 @@ module Api verification = RegistrantVerification.new(domain_id: @domain.id, verification_token: verify_params[:token]) - head(:bad_request) and return unless update_action(verification) + unless delete_action? ? delete_action(verification) : change_action(verification) + head :bad_request + return + end render json: { domain_name: @domain.name, @@ -32,21 +35,28 @@ module Api private - def current_registrant - changes_registrant? ? @domain.registrant : @domain.pending_registrant + def initiator + "email link, #{I18n.t(:user_not_authenticated)}" end - def changes_registrant? + def current_registrant + approved? ? @domain.registrant : @domain.pending_registrant + end + + def approved? params[:decision] == 'confirmed' end - def update_action(verification) - initiator = "email link, #{I18n.t(:user_not_authenticated)}" - if changes_registrant? - verification.domain_registrant_change_confirm!(initiator) - else - verification.domain_registrant_change_reject!(initiator) - end + def change_action(verification) + return verification.domain_registrant_change_confirm!(initiator) if approved? + + verification.domain_registrant_change_reject!(initiator) + end + + def delete_action(verification) + return verification.domain_registrant_delete_confirm!(initiator) if approved? + + verification.domain_registrant_delete_reject!(initiator) end def serialized_registrant(registrant) @@ -59,11 +69,18 @@ module Api def verify_params params do |p| + p.require(:template) p.require(:name) p.require(:token) end end + def delete_action? + return true if params[:template] == 'delete' + + false + end + def verify_decision return if %w[confirmed rejected].include?(params[:decision]) @@ -78,8 +95,12 @@ module Api render json: { error: 'Domain not found' }, status: :not_found end - def verify_updateable - return if @domain.registrant_update_confirmable?(verify_params[:token]) + def verify_action + if params[:template] == 'change' + return true if @domain.registrant_update_confirmable?(verify_params[:token]) + elsif params[:template] == 'delete' + return true if @domain.registrant_delete_confirmable?(verify_params[:token]) + end render json: { error: 'Application expired or not found' }, status: :unauthorized end diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index c4190fe14..8e2b1a341 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -57,7 +57,7 @@ class DomainDeleteMailer < ApplicationMailer if base_url.blank? registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) else - "#{base_url}/confirmation/#{domain.name_puny}/#{domain.registrant_verification_token}" + "#{base_url}/confirmation/#{domain.name_puny}/delete/#{domain.registrant_verification_token}" end end diff --git a/app/mailers/registrant_change_mailer.rb b/app/mailers/registrant_change_mailer.rb index ff3cfa18e..3e97f4b86 100644 --- a/app/mailers/registrant_change_mailer.rb +++ b/app/mailers/registrant_change_mailer.rb @@ -50,7 +50,12 @@ class RegistrantChangeMailer < ApplicationMailer private def confirmation_url(domain) - registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) + base_url = ENV['registrant_portal_verifications_base_url'] + if base_url.blank? + registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) + else + "#{base_url}/confirmation/#{domain.name_puny}/change/#{domain.registrant_verification_token}" + end end def address_processing diff --git a/config/routes.rb b/config/routes.rb index 7061f125f..440c9c05e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,8 +56,8 @@ Rails.application.routes.draw do namespace :v1 do namespace :registrant do post 'auth/eid', to: 'auth#eid' - get 'confirms/:name/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } - post 'confirms/:name/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } + get 'confirms/:name/:template/:token', to: 'confirms#index', constraints: { name: /[^\/]+/ } + post 'confirms/:name/:template/:token/:decision', to: 'confirms#update', constraints: { name: /[^\/]+/ } resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] From c42c482d64f61ca27af202dce82ef7e1e3a6d9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 13:38:09 +0200 Subject: [PATCH 092/202] Fix CC issues --- .../api/v1/registrant/confirms_controller.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index b04a72449..d03e7ab93 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -26,11 +26,9 @@ module Api return end - render json: { - domain_name: @domain.name, - current_registrant: serialized_registrant(current_registrant), - status: params[:decision], - } + render json: { domain_name: @domain.name, + current_registrant: serialized_registrant(current_registrant), + status: params[:decision] } end private @@ -96,11 +94,13 @@ module Api end def verify_action - if params[:template] == 'change' - return true if @domain.registrant_update_confirmable?(verify_params[:token]) - elsif params[:template] == 'delete' - return true if @domain.registrant_delete_confirmable?(verify_params[:token]) - end + action = if params[:template] == 'change' + @domain.registrant_update_confirmable?(verify_params[:token]) + elsif params[:template] == 'delete' + @domain.registrant_delete_confirmable?(verify_params[:token]) + end + + return unless action render json: { error: 'Application expired or not found' }, status: :unauthorized end From 60a66bc540d30487e58886576e7d1e283c8ae2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 15:46:55 +0200 Subject: [PATCH 093/202] Registrant confirms API: Add tests --- .../api/v1/registrant/confirms_controller.rb | 39 ++- .../registrant_api_verifications_test.rb | 259 ++++++++++++++++++ 2 files changed, 283 insertions(+), 15 deletions(-) create mode 100644 test/integration/api/registrant/registrant_api_verifications_test.rb diff --git a/app/controllers/api/v1/registrant/confirms_controller.rb b/app/controllers/api/v1/registrant/confirms_controller.rb index d03e7ab93..057400c8e 100644 --- a/app/controllers/api/v1/registrant/confirms_controller.rb +++ b/app/controllers/api/v1/registrant/confirms_controller.rb @@ -10,11 +10,16 @@ module Api before_action :verify_decision, only: %i[update] def index - render json: { + res = { domain_name: @domain.name, current_registrant: serialized_registrant(@domain.registrant), - new_registrant: serialized_registrant(@domain.pending_registrant), } + + unless delete_action? + res[:new_registrant] = serialized_registrant(@domain.pending_registrant) + end + + render json: res, status: :ok end def update @@ -28,7 +33,7 @@ module Api render json: { domain_name: @domain.name, current_registrant: serialized_registrant(current_registrant), - status: params[:decision] } + status: params[:decision] }, status: :ok end private @@ -38,23 +43,27 @@ module Api end def current_registrant - approved? ? @domain.registrant : @domain.pending_registrant + confirmed? && !delete_action? ? @domain.pending_registrant : @domain.registrant end - def approved? - params[:decision] == 'confirmed' + def confirmed? + verify_params[:decision] == 'confirmed' end def change_action(verification) - return verification.domain_registrant_change_confirm!(initiator) if approved? - - verification.domain_registrant_change_reject!(initiator) + if confirmed? + verification.domain_registrant_change_confirm!(initiator) + else + verification.domain_registrant_change_reject!(initiator) + end end def delete_action(verification) - return verification.domain_registrant_delete_confirm!(initiator) if approved? - - verification.domain_registrant_delete_reject!(initiator) + if confirmed? + verification.domain_registrant_delete_confirm!(initiator) + else + verification.domain_registrant_delete_reject!(initiator) + end end def serialized_registrant(registrant) @@ -67,9 +76,9 @@ module Api def verify_params params do |p| - p.require(:template) p.require(:name) p.require(:token) + p.permit(:decision) end end @@ -82,7 +91,7 @@ module Api def verify_decision return if %w[confirmed rejected].include?(params[:decision]) - head :bad_request + head :not_found end def set_domain @@ -100,7 +109,7 @@ module Api @domain.registrant_delete_confirmable?(verify_params[:token]) end - return unless action + return if action render json: { error: 'Application expired or not found' }, status: :unauthorized end diff --git a/test/integration/api/registrant/registrant_api_verifications_test.rb b/test/integration/api/registrant/registrant_api_verifications_test.rb new file mode 100644 index 000000000..b2333e560 --- /dev/null +++ b/test/integration/api/registrant/registrant_api_verifications_test.rb @@ -0,0 +1,259 @@ +require 'test_helper' +require 'auth_token/auth_token_creator' + +class RegistrantApiVerificationsTest < ApplicationIntegrationTest + def setup + super + + @domain = domains(:hospital) + @registrant = @domain.registrant + @new_registrant = contacts(:jack) + + @token = 'verysecrettoken' + + @domain.update(statuses: [DomainStatus::PENDING_UPDATE], + registrant_verification_asked_at: Time.zone.now - 1.day, + registrant_verification_token: @token) + + end + + def test_fetches_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: "hospital.test", + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + new_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + } + } + + assert_equal expected_body, res + end + + def test_approves_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + }, + status: 'confirmed' + } + + assert_equal expected_body, res + end + + def test_rejects_registrant_change_request + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + assert @domain.registrant_update_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/rejected" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'rejected' + } + + assert_equal expected_body, res + end + + def test_registrant_change_requires_valid_attributes + pending_json = { new_registrant_id: @new_registrant.id } + @domain.update(pending_json: pending_json) + @domain.reload + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/change/123" + assert_equal 401, response.status + + get "/api/v1/registrant/confirms/aohldfjg.ee/change/123" + assert_equal 404, response.status + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/invalidaction" + assert_equal 404, response.status + end + + def test_fetches_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: "hospital.test", + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + } + } + + assert_equal expected_body, res + end + + def test_approves_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/confirmed" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'confirmed' + } + + assert_equal expected_body, res + end + + def test_rejects_domain_delete_request + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload + + assert @domain.registrant_delete_confirmable?(@token) + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/rejected" + assert_equal(200, response.status) + + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @registrant.name, + ident: @registrant.ident, + country: @registrant.ident_country_code + }, + status: 'rejected' + } + + assert_equal expected_body, res + end + + def test_domain_delete_requires_valid_attributes + @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE]) + @domain.reload + + get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/123" + assert_equal 401, response.status + + get "/api/v1/registrant/confirms/aohldfjg.ee/delete/123" + assert_equal 404, response.status + + post "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/#{@token}/invalidaction" + assert_equal 404, response.status + end + #def test_get_non_existent_domain_details_by_uuid + # get '/api/v1/registrant/domains/random-uuid', headers: @auth_headers + # assert_equal(404, response.status) + + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [base: ['Domain not found']] }, response_json) + #end + + #def test_root_returns_domain_list + # get '/api/v1/registrant/domains', headers: @auth_headers + # assert_equal(200, response.status) + + # response_json = JSON.parse(response.body, symbolize_names: true) + # array_of_domain_names = response_json.map { |x| x[:name] } + # assert(array_of_domain_names.include?('hospital.test')) + + # array_of_domain_registrars = response_json.map { |x| x[:registrar] } + # assert(array_of_domain_registrars.include?({name: 'Good Names', website: nil})) + #end + + #def test_root_accepts_limit_and_offset_parameters + # get '/api/v1/registrant/domains', params: { 'limit' => 2, 'offset' => 0 }, + # headers: @auth_headers + # response_json = JSON.parse(response.body, symbolize_names: true) + + # assert_equal(200, response.status) + # assert_equal(2, response_json.count) + + # get '/api/v1/registrant/domains', headers: @auth_headers + # response_json = JSON.parse(response.body, symbolize_names: true) + + # assert_equal(4, response_json.count) + #end + + #def test_root_does_not_accept_limit_higher_than_200 + # get '/api/v1/registrant/domains', params: { 'limit' => 400, 'offset' => 0 }, + # headers: @auth_headers + + # assert_equal(400, response.status) + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [{ limit: ['parameter is out of range'] }] }, response_json) + #end + + #def test_root_does_not_accept_offset_lower_than_0 + # get '/api/v1/registrant/domains', params: { 'limit' => 200, 'offset' => "-10" }, + # headers: @auth_headers + + # assert_equal(400, response.status) + # response_json = JSON.parse(response.body, symbolize_names: true) + # assert_equal({ errors: [{ offset: ['parameter is out of range'] }] }, response_json) + #end + + #def test_root_returns_401_without_authorization + # get '/api/v1/registrant/domains' + # assert_equal(401, response.status) + # json_body = JSON.parse(response.body, symbolize_names: true) + + # assert_equal({ errors: [base: ['Not authorized']] }, json_body) + #end + + #def test_details_returns_401_without_authorization + # get '/api/v1/registrant/domains/5edda1a5-3548-41ee-8b65-6d60daf85a37' + # assert_equal(401, response.status) + # json_body = JSON.parse(response.body, symbolize_names: true) + + # assert_equal({ errors: [base: ['Not authorized']] }, json_body) + #end +end From 54eb1e91decdc65d880ae1f44d0ca6ab135f1f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 12 Nov 2020 17:05:13 +0200 Subject: [PATCH 094/202] Fix CC issues --- app/mailers/application_mailer.rb | 13 ++- app/mailers/domain_delete_mailer.rb | 11 +-- app/mailers/registrant_change_mailer.rb | 11 +-- .../registrant_api_verifications_test.rb | 90 +++++++++++++------ 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 9269a1102..9366174ef 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,15 @@ class ApplicationMailer < ActionMailer::Base append_view_path Rails.root.join('app', 'views', 'mailers') layout 'mailer' -end \ No newline at end of file + + def registrant_confirm_url(domain:, method:) + token = domain.registrant_verification_token + base_url = ENV['registrant_portal_verifications_base_url'] + + url = registrant_domain_delete_confirm_url(domain, token: token) if method == 'delete' + url ||= registrant_domain_update_confirm_url(domain, token: token) + return url if base_url.blank? + + "#{base_url}/confirms/#{domain.name_puny}/#{method}/#{token}" + end +end diff --git a/app/mailers/domain_delete_mailer.rb b/app/mailers/domain_delete_mailer.rb index 8e2b1a341..8c0e830b1 100644 --- a/app/mailers/domain_delete_mailer.rb +++ b/app/mailers/domain_delete_mailer.rb @@ -6,7 +6,7 @@ class DomainDeleteMailer < ApplicationMailer def confirmation_request(domain:, registrar:, registrant:) @domain = DomainPresenter.new(domain: domain, view: view_context) @registrar = RegistrarPresenter.new(registrar: registrar, view: view_context) - @confirmation_url = confirmation_url(domain) + @confirmation_url = registrant_confirm_url(domain: domain, method: 'delete') subject = default_i18n_subject(domain_name: domain.name) mail(to: registrant.email, subject: subject) @@ -52,15 +52,6 @@ class DomainDeleteMailer < ApplicationMailer private - def confirmation_url(domain) - base_url = ENV['registrant_portal_verifications_base_url'] - if base_url.blank? - registrant_domain_delete_confirm_url(domain, token: domain.registrant_verification_token) - else - "#{base_url}/confirmation/#{domain.name_puny}/delete/#{domain.registrant_verification_token}" - end - end - def forced_email_from ENV['action_mailer_force_delete_from'] || self.class.default[:from] end diff --git a/app/mailers/registrant_change_mailer.rb b/app/mailers/registrant_change_mailer.rb index 3e97f4b86..8f43f4ab5 100644 --- a/app/mailers/registrant_change_mailer.rb +++ b/app/mailers/registrant_change_mailer.rb @@ -5,7 +5,7 @@ class RegistrantChangeMailer < ApplicationMailer @domain = DomainPresenter.new(domain: domain, view: view_context) @registrar = RegistrarPresenter.new(registrar: registrar, view: view_context) @new_registrant = RegistrantPresenter.new(registrant: new_registrant, view: view_context) - @confirmation_url = confirmation_url(domain) + @confirmation_url = registrant_confirm_url(domain: domain, method: 'change') subject = default_i18n_subject(domain_name: domain.name) mail(to: current_registrant.email, subject: subject) @@ -49,15 +49,6 @@ class RegistrantChangeMailer < ApplicationMailer private - def confirmation_url(domain) - base_url = ENV['registrant_portal_verifications_base_url'] - if base_url.blank? - registrant_domain_update_confirm_url(domain, token: domain.registrant_verification_token) - else - "#{base_url}/confirmation/#{domain.name_puny}/change/#{domain.registrant_verification_token}" - end - end - def address_processing Contact.address_processing? end diff --git a/test/integration/api/registrant/registrant_api_verifications_test.rb b/test/integration/api/registrant/registrant_api_verifications_test.rb index b2333e560..821d0dee0 100644 --- a/test/integration/api/registrant/registrant_api_verifications_test.rb +++ b/test/integration/api/registrant/registrant_api_verifications_test.rb @@ -8,17 +8,22 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest @domain = domains(:hospital) @registrant = @domain.registrant @new_registrant = contacts(:jack) + @user = users(:api_bestnames) @token = 'verysecrettoken' - @domain.update(statuses: [DomainStatus::PENDING_UPDATE], + @domain.update!(statuses: [DomainStatus::PENDING_UPDATE], registrant_verification_asked_at: Time.zone.now - 1.day, registrant_verification_token: @token) end def test_fetches_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -46,31 +51,40 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_approves_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } - @domain.update(pending_json: pending_json) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update!(pending_json: pending_json) @domain.reload assert @domain.registrant_update_confirmable?(@token) - post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" - assert_equal(200, response.status) + perform_enqueued_jobs do + post "/api/v1/registrant/confirms/#{@domain.name_puny}/change/#{@token}/confirmed" + assert_equal(200, response.status) - res = JSON.parse(response.body, symbolize_names: true) - expected_body = { - domain_name: @domain.name, - current_registrant: { - name: @new_registrant.name, - ident: @new_registrant.ident, - country: @new_registrant.ident_country_code - }, - status: 'confirmed' - } - - assert_equal expected_body, res + res = JSON.parse(response.body, symbolize_names: true) + expected_body = { + domain_name: @domain.name, + current_registrant: { + name: @new_registrant.name, + ident: @new_registrant.ident, + country: @new_registrant.ident_country_code + }, + status: 'confirmed' + } + assert_equal expected_body, res + end end def test_rejects_registrant_change_request - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -94,7 +108,11 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_registrant_change_requires_valid_attributes - pending_json = { new_registrant_id: @new_registrant.id } + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + @domain.update(pending_json: pending_json) @domain.reload @@ -109,7 +127,12 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_fetches_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -131,8 +154,13 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_approves_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - @domain.reload + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -154,8 +182,13 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_rejects_domain_delete_request - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - @domain.reload + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) + @domain.reload assert @domain.registrant_delete_confirmable?(@token) @@ -177,7 +210,12 @@ class RegistrantApiVerificationsTest < ApplicationIntegrationTest end def test_domain_delete_requires_valid_attributes - @domain.update(statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE]) + pending_json = { new_registrant_id: @new_registrant.id, + new_registrant_name: @new_registrant.name, + new_registrant_email: @new_registrant.email, + current_user_id: @user.id } + + @domain.update(pending_json: pending_json, statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) @domain.reload get "/api/v1/registrant/confirms/#{@domain.name_puny}/delete/123" From fc816ad67ba143804764ddaeca8721d6e4accbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 13 Nov 2020 16:38:39 +0200 Subject: [PATCH 095/202] REPP: Check for ident hash presence before contact update --- app/controllers/repp/v1/contacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index a6cd7d07d..504948ed9 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -108,7 +108,7 @@ module Repp end def contact_ident_params(required: true) - if required + if required || !params[:contact][:ident].nil? params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else From 3d0150076c83eba56d474f89557750f8879ba9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 16 Nov 2020 14:46:37 +0200 Subject: [PATCH 096/202] REPP: Hide contact internal ID --- .../repp/v1/contacts_controller.rb | 16 ++++--- app/models/epp/contact.rb | 2 +- lib/serializers/repp/contact.rb | 42 +++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 lib/serializers/repp/contact.rb diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 504948ed9..69d4ecc96 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -1,3 +1,4 @@ +require 'serializers/repp/contact' module Repp module V1 class ContactsController < BaseController @@ -14,7 +15,8 @@ module Repp ## GET /repp/v1/contacts/1 def show - render_success(data: @contact.as_json) + serializer = ::Serializers::Repp::Contact.new(@contact, show_address: Contact.address_processing?) + render_success(data: serializer.to_json) end ## GET /repp/v1/contacts/check/1 @@ -76,11 +78,13 @@ module Repp def showable_contacts(details, limit, offset) contacts = current_user.registrar.contacts.limit(limit).offset(offset) - unless Contact.address_processing? && params[:details] == 'true' - contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) - end - contacts = contacts.pluck(:code) unless details + return contacts.pluck(:code) unless details + + contacts = contacts.map do |contact| + serializer = ::Serializers::Repp::Contact.new(contact, show_address: Contact.address_processing?) + serializer.to_json + end contacts end @@ -104,7 +108,7 @@ module Repp def contact_create_params(required: true) params.require(:contact).require(%i[name email phone]) if required - params.require(:contact).permit(:name, :email, :phone, :code) + params.require(:contact).permit(:name, :email, :phone, :id) end def contact_ident_params(required: true) diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 3f0f3e8ab..50ebac065 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -36,7 +36,7 @@ class Epp::Contact < Contact attrs = epp ? attrs_from(frame, new_record: true) : frame super( attrs.merge( - code: epp ? frame.css('id').text : frame[:code], + code: epp ? frame.css('id').text : frame[:id], registrar: registrar ) ) diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb new file mode 100644 index 000000000..fe08a6f7d --- /dev/null +++ b/lib/serializers/repp/contact.rb @@ -0,0 +1,42 @@ +module Serializers + module Repp + class Contact + attr_reader :contact + + def initialize(contact, show_address:) + @contact = contact + @show_address = show_address + end + + def to_json + json = { + id: contact.code, + name: contact.name, + ident: { + code: contact.ident, + type: contact.ident_type, + country_code: contact.ident_country_code, + }, + email: contact.email, + phone: contact.phone, + fax: contact.fax, + auth_info: contact.auth_info, + statuses: contact.statuses, + disclosed_attributes: contact.disclosed_attributes, + } + + return json unless @show_address + + json[:address] = { + street: contact.street, + zip: contact.zip, + city: contact.city, + state: contact.state, + country_code: contact.country_code, + } + + json + end + end + end +end From 03db562c77f9020dad4b61a3bba80b6baa0fa93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 16 Nov 2020 16:26:59 +0200 Subject: [PATCH 097/202] REPP: Show error when invalid ident format submitted --- app/controllers/repp/v1/base_controller.rb | 1 + app/controllers/repp/v1/contacts_controller.rb | 2 +- app/models/actions/contact_update.rb | 8 ++++++-- test/integration/repp/v1/contacts/show_test.rb | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 678ae0a22..70b189fc1 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,6 +4,7 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction + before_action :set_paper_trail_whodunnit attr_reader :current_user rescue_from ActionController::ParameterMissing do |exception| diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 69d4ecc96..959d4f6c7 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -112,7 +112,7 @@ module Repp end def contact_ident_params(required: true) - if required || !params[:contact][:ident].nil? + if required params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) else diff --git a/app/models/actions/contact_update.rb b/app/models/actions/contact_update.rb index 6400526c5..7ca7b6b04 100644 --- a/app/models/actions/contact_update.rb +++ b/app/models/actions/contact_update.rb @@ -17,7 +17,7 @@ module Actions def call maybe_remove_address maybe_update_statuses - maybe_update_ident if ident + maybe_update_ident if ident.present? maybe_attach_legal_doc commit end @@ -53,7 +53,11 @@ module Actions end def maybe_update_ident - return unless ident[:ident] + unless ident.is_a?(Hash) + contact.add_epp_error('2308', nil, nil, I18n.t('epp.contacts.errors.valid_ident')) + @error = true + return + end if contact.identifier.valid? submitted_ident = ::Contact::Ident.new(code: ident[:ident], diff --git a/test/integration/repp/v1/contacts/show_test.rb b/test/integration/repp/v1/contacts/show_test.rb index 3fd782cca..4a6f5b615 100644 --- a/test/integration/repp/v1/contacts/show_test.rb +++ b/test/integration/repp/v1/contacts/show_test.rb @@ -28,7 +28,7 @@ class ReppV1ContactsShowTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert_equal contact.code, json[:data][:code] + assert_equal contact.code, json[:data][:id] end def test_can_not_access_out_of_scope_contacts From 7edf48c885e6bced344f125c8e8304a04866f0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 11:51:45 +0200 Subject: [PATCH 098/202] Fix some CC issues --- .../repp/v1/contacts_controller.rb | 6 ++- .../v1/registrar/nameservers_controller.rb | 2 +- lib/serializers/registrant_api/.DS_Store | Bin 0 -> 6148 bytes lib/serializers/repp/contact.rb | 44 ++++++++---------- 4 files changed, 24 insertions(+), 28 deletions(-) create mode 100644 lib/serializers/registrant_api/.DS_Store diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 959d4f6c7..144be01c6 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -15,7 +15,8 @@ module Repp ## GET /repp/v1/contacts/1 def show - serializer = ::Serializers::Repp::Contact.new(@contact, show_address: Contact.address_processing?) + serializer = ::Serializers::Repp::Contact.new(@contact, + show_address: Contact.address_processing?) render_success(data: serializer.to_json) end @@ -82,7 +83,8 @@ module Repp return contacts.pluck(:code) unless details contacts = contacts.map do |contact| - serializer = ::Serializers::Repp::Contact.new(contact, show_address: Contact.address_processing?) + serializer = ::Serializers::Repp::Contact.new(contact, + show_address: Contact.address_processing?) serializer.to_json end diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index fb00d92c0..47004d97b 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -34,7 +34,7 @@ module Repp params.permit(data: [ :type, :id, { domains: [], - attributes: [:hostname, { ipv4: [], ipv6: [] }] }, + attributes: [:hostname, { ipv4: [], ipv6: [] }] } ]) end diff --git a/lib/serializers/registrant_api/.DS_Store b/lib/serializers/registrant_api/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Tue, 17 Nov 2020 13:00:47 +0200 Subject: [PATCH 099/202] Add response to REPP log --- .../repp/v1/auctions_controller.rb | 4 +-- app/controllers/repp/v1/base_controller.rb | 29 +++++++------------ .../repp/v1/contacts_controller.rb | 4 +-- .../repp/v1/domains/contacts_controller.rb | 4 +-- .../repp/v1/retained_domains_controller.rb | 3 +- lib/serializers/repp/contact.rb | 10 +++---- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/app/controllers/repp/v1/auctions_controller.rb b/app/controllers/repp/v1/auctions_controller.rb index 4a5265d13..676dac266 100644 --- a/app/controllers/repp/v1/auctions_controller.rb +++ b/app/controllers/repp/v1/auctions_controller.rb @@ -3,9 +3,9 @@ module Repp class AuctionsController < ActionController::API def index auctions = Auction.started + @response = { count: auctions.count, auctions: auctions_to_json(auctions) } - render json: { count: auctions.count, - auctions: auctions_to_json(auctions) } + render json: @response end private diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 70b189fc1..73d8c400a 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -24,10 +24,10 @@ module Repp private def render_success(code: nil, message: nil, data: nil) - resp = { code: code || 1000, message: message || 'Command completed successfully', + @response = { code: code || 1000, message: message || 'Command completed successfully', data: data || {} } - render(json: resp, status: :ok) + render(json: @response, status: :ok) end def epp_errors @@ -64,10 +64,8 @@ module Repp @epp_errors ||= [] @epp_errors << { code: 2304, msg: 'Command failed' } if data != {} - render( - json: { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data }, - status: status - ) + @response = { code: @epp_errors[0][:code].to_i, message: @epp_errors[0][:msg], data: data } + render(json: @response, status: status) end def basic_token @@ -85,10 +83,8 @@ module Repp raise(ArgumentError) rescue NoMethodError, ArgumentError - render( - json: { code: 2202, message: 'Invalid authorization information' }, - status: :unauthorized - ) + @response = { code: 2202, message: 'Invalid authorization information' } + render(json: @response, status: :unauthorized) end def check_ip_restriction @@ -96,17 +92,14 @@ module Repp return if allowed - render( - json: { - code: 2202, - message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip), - }, - status: :unauthorized - ) + @response = { code: 2202, + message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip) } + render(json: @response, status: :unauthorized) end def not_found_error - render(json: { code: 2303, message: 'Object does not exist' }, status: :not_found) + @response = { code: 2303, message: 'Object does not exist' } + render(json: @response, status: :not_found) end end end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb index 144be01c6..acf275c47 100644 --- a/app/controllers/repp/v1/contacts_controller.rb +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -9,8 +9,8 @@ module Repp record_count = current_user.registrar.contacts.count contacts = showable_contacts(params[:details], params[:limit] || 200, params[:offset] || 0) - - render(json: { contacts: contacts, total_number_of_records: record_count }, status: :ok) + @response = { contacts: contacts, total_number_of_records: record_count } + render(json: @response, status: :ok) end ## GET /repp/v1/contacts/1 diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index fb5dfb567..75404e0c6 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -26,8 +26,8 @@ module Repp return handle_errors if @epp_errors.any? affected, skipped = TechDomainContact.replace(@current_contact, @new_contact) - data = { affected_domains: affected, skipped_domains: skipped } - render_success(data: data) + @response = { affected_domains: affected, skipped_domains: skipped } + render_success(data: @response) end private diff --git a/app/controllers/repp/v1/retained_domains_controller.rb b/app/controllers/repp/v1/retained_domains_controller.rb index c1bb458e9..8edc32f5b 100644 --- a/app/controllers/repp/v1/retained_domains_controller.rb +++ b/app/controllers/repp/v1/retained_domains_controller.rb @@ -3,8 +3,9 @@ module Repp class RetainedDomainsController < ActionController::API def index domains = RetainedDomains.new(query_params) + @response = { count: domains.count, domains: domains.to_jsonable } - render json: { count: domains.count, domains: domains.to_jsonable } + render json: @response end def query_params diff --git a/lib/serializers/repp/contact.rb b/lib/serializers/repp/contact.rb index 8a3cad616..834402359 100644 --- a/lib/serializers/repp/contact.rb +++ b/lib/serializers/repp/contact.rb @@ -8,11 +8,11 @@ module Serializers @show_address = show_address end - def to_json(_obj) - json = { id: contact.code, name: contact.name, ident: ident, - email: contact.email, phone: contact.phone, fax: contact.fax, - auth_info: contact.auth_info, statuses: contact.statuses, - disclosed_attributes: contact.disclosed_attributes } + def to_json(obj = contact) + json = { id: obj.code, name: obj.name, ident: ident, + email: obj.email, phone: obj.phone, fax: obj.fax, + auth_info: obj.auth_info, statuses: obj.statuses, + disclosed_attributes: obj.disclosed_attributes } json[:address] = address if @show_address From e40737408f9fec6eaf1400f31a530936e457247c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 14:33:12 +0200 Subject: [PATCH 100/202] History: Show last state of contact on contact destroy event --- app/helpers/object_versions_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index d8e00abbe..8e394b29a 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -3,7 +3,8 @@ module ObjectVersionsHelper version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, value.last) + delete_action = version.event == 'destroy' + new_object.public_send(method_name, delete_action ? value.first : value.last) end end end From c9ebdbeb40e1f4377e30ee5ca0cdf733ead11554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 14:40:18 +0200 Subject: [PATCH 101/202] EPP: Hide name from contactInfo if no auth provided --- app/views/epp/contacts/info.xml.builder | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 1945e7def..4874080e8 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -14,7 +14,11 @@ xml.epp_head do end xml.tag!('contact:postalInfo', type: 'int') do - xml.tag!('contact:name', @contact.name) + if can? :view_full_info, @contact, @password + xml.tag!('contact:name', @contact.name) + else + xml.tag!('contact:name', 'No access') + end if can? :view_full_info, @contact, @password xml.tag!('contact:org', @contact.org_name) if @contact.org_name.present? From a747464f3d8d076aaebce2246111c4d47e370c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 16:16:27 +0200 Subject: [PATCH 102/202] Don't show contact name if not viewed by sponsoring registrar --- app/views/registrar/domains/partials/_contacts.haml | 2 +- app/views/registrar/domains/partials/_general.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/registrar/domains/partials/_contacts.haml b/app/views/registrar/domains/partials/_contacts.haml index 48d1ac21f..e6ef9aa8f 100644 --- a/app/views/registrar/domains/partials/_contacts.haml +++ b/app/views/registrar/domains/partials/_contacts.haml @@ -13,5 +13,5 @@ - registrant = Contact.find_by_code(x.text) %tr %td= x['type'] - %td= registrant.name + %td= registrant.registrar == current_registrar_user.registrar ? registrant.name : 'N/A' %td= x.text diff --git a/app/views/registrar/domains/partials/_general.html.erb b/app/views/registrar/domains/partials/_general.html.erb index 3fb3a5df8..ff064857c 100644 --- a/app/views/registrar/domains/partials/_general.html.erb +++ b/app/views/registrar/domains/partials/_general.html.erb @@ -23,7 +23,7 @@ <% registrant = Contact.find_by_code(@data.css('registrant').text) %>
<%= t('.registrant') %>
-
<%= "#{registrant.name} (#{@data.css('registrant').text})" %>
+
<%= registrant.registrar == current_registrar_user.registrar ? "#{registrant.name} (#{@data.css('registrant').text})" : @data.css('registrant').text %>
<%= t('.registered') %>
<%= @data.css('crDate').text %>
From a6b6a3e60ad3319073c355bce51472a4362dc1f1 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 17 Nov 2020 19:37:24 +0500 Subject: [PATCH 103/202] Enable WhiteIp for checking against subnets --- app/models/white_ip.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 303ff5886..e86f62ea7 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -24,7 +24,10 @@ class WhiteIp < ApplicationRecord class << self def include_ip?(ip) - where('ipv4 = :ip OR ipv6 = :ip', ip: ip).any? + ipv4 = select{ |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select{ |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4.pluck(:id) + ipv6.pluck(:id)).flatten.uniq + where(id: ids).any? end end end From 5cc85980d448cf71cd1bb2ef218bc2b9ef5138a0 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 17 Nov 2020 19:46:10 +0500 Subject: [PATCH 104/202] Fixed validations --- app/models/white_ip.rb | 28 ++++++++++++++++++++----- test/system/registrar_area/base_test.rb | 10 +++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index e86f62ea7..c2fb51cd1 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -2,8 +2,8 @@ class WhiteIp < ApplicationRecord include Versions belongs_to :registrar - validates :ipv4, format: { with: /\A(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/, allow_blank: true } - validates :ipv6, format: { with: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, allow_blank: true } + validate :valid_ipv4? + validate :valid_ipv6? validate :validate_ipv4_and_ipv6 def validate_ipv4_and_ipv6 @@ -11,6 +11,22 @@ class WhiteIp < ApplicationRecord errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present)) end + def valid_ipv4? + return if ipv4.blank? + + IPAddr.new(ipv4, Socket::AF_INET) + rescue StandardError => _e + errors.add(:ipv4, :invalid) + end + + def valid_ipv6? + return if ipv6.blank? + + IPAddr.new(ipv6, Socket::AF_INET6) + rescue StandardError => _e + errors.add(:ipv6, :invalid) + end + API = 'api' REGISTRAR = 'registrar' INTERFACES = [API, REGISTRAR] @@ -23,11 +39,13 @@ class WhiteIp < ApplicationRecord end class << self + # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select{ |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select{ |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4.pluck(:id) + ipv6.pluck(:id)).flatten.uniq + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4 + ipv6).pluck(:id).flatten.uniq where(id: ids).any? end + # rubocop:enable Style/CaseEquality end end diff --git a/test/system/registrar_area/base_test.rb b/test/system/registrar_area/base_test.rb index 67b19a044..2529b009a 100644 --- a/test/system/registrar_area/base_test.rb +++ b/test/system/registrar_area/base_test.rb @@ -29,6 +29,16 @@ class RegistrarAreaBaseTestTest < ApplicationSystemTestCase assert_button 'Login' end + def test_user_can_access_when_ip_is_whitelisted_with_subnet + white_ips(:one).update!(ipv4: '127.0.0.1/32', interfaces: [WhiteIp::REGISTRAR]) + Setting.registrar_ip_whitelist_enabled = true + + visit new_registrar_user_session_url + + assert_no_text 'Access denied from IP 127.0.0.1' + assert_button 'Login' + end + def test_user_can_access_when_ip_is_not_whitelisted_and_whitelist_is_disabled Setting.registrar_ip_whitelist_enabled = false WhiteIp.delete_all From 7d08e22f6fe1ca0f6419907303d36fcdd3925d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 17 Nov 2020 16:55:07 +0200 Subject: [PATCH 105/202] Fix CC issues --- app/controllers/repp/v1/base_controller.rb | 2 +- app/helpers/object_versions_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 73d8c400a..4bfa61338 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -25,7 +25,7 @@ module Repp def render_success(code: nil, message: nil, data: nil) @response = { code: code || 1000, message: message || 'Command completed successfully', - data: data || {} } + data: data || {} } render(json: @response, status: :ok) end diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index 8e394b29a..fcec7c7d9 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -1,10 +1,10 @@ module ObjectVersionsHelper def attach_existing_fields(version, new_object) + destroy_event = version.event == 'destroy' version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - delete_action = version.event == 'destroy' - new_object.public_send(method_name, delete_action ? value.first : value.last) + new_object.public_send(method_name, destroy_event ? value.first : value.last) end end end From 56b5d8e275727080d27835a82bbe7be9e1c1ecb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 10:56:57 +0200 Subject: [PATCH 106/202] Fix registrant verificication URL building --- app/mailers/application_mailer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 9366174ef..af5d59d63 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -10,6 +10,6 @@ class ApplicationMailer < ActionMailer::Base url ||= registrant_domain_update_confirm_url(domain, token: token) return url if base_url.blank? - "#{base_url}/confirms/#{domain.name_puny}/#{method}/#{token}" + "#{base_url}/confirmation/#{domain.name_puny}/#{method}/#{token}" end end From 38f86225ca16a294e1deacd8f4383059a724f922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 12:35:11 +0200 Subject: [PATCH 107/202] Add tests, show papertrail whodunnit properly on repp api --- app/controllers/repp/v1/base_controller.rb | 7 ++++++- app/helpers/object_versions_helper.rb | 9 +++++++-- test/integration/repp/v1/contacts/update_test.rb | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb index 4bfa61338..ca9ef6fb5 100644 --- a/app/controllers/repp/v1/base_controller.rb +++ b/app/controllers/repp/v1/base_controller.rb @@ -4,9 +4,10 @@ module Repp rescue_from ActiveRecord::RecordNotFound, with: :not_found_error before_action :authenticate_user before_action :check_ip_restriction - before_action :set_paper_trail_whodunnit attr_reader :current_user + before_action :set_paper_trail_whodunnit + rescue_from ActionController::ParameterMissing do |exception| render json: { code: 2003, message: exception }, status: :bad_request end @@ -23,6 +24,10 @@ module Repp private + def set_paper_trail_whodunnit + ::PaperTrail.request.whodunnit = current_user + end + def render_success(code: nil, message: nil, data: nil) @response = { code: code || 1000, message: message || 'Command completed successfully', data: data || {} } diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index fcec7c7d9..dccf03017 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -1,10 +1,9 @@ module ObjectVersionsHelper def attach_existing_fields(version, new_object) - destroy_event = version.event == 'destroy' version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, destroy_event ? value.first : value.last) + new_object.public_send(method_name, event_value(event, value)) end end end @@ -13,4 +12,10 @@ module ObjectVersionsHelper field_names = model.column_names version.object.to_h.select { |key, _value| field_names.include?(key) } end + + private + + def event_value(version, val) + version.event == 'destroy' ? val.first : val.last + end end diff --git a/test/integration/repp/v1/contacts/update_test.rb b/test/integration/repp/v1/contacts/update_test.rb index dd762e341..cf27f98da 100644 --- a/test/integration/repp/v1/contacts/update_test.rb +++ b/test/integration/repp/v1/contacts/update_test.rb @@ -101,4 +101,19 @@ class ReppV1ContactsUpdateTest < ActionDispatch::IntegrationTest @contact.reload assert @contact.legal_documents.any? end + + def test_returns_error_if_ident_wrong_format + request_body = { + "contact": { + "ident": "123" + } + } + + put "/repp/v1/contacts/#{@contact.code}", headers: @auth_headers, params: request_body + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2308, json[:code] + assert_equal 'Ident update is not allowed. Consider creating new contact object', json[:message] + end end From c7d64dce37c3dc5498c142a07e6963bbcab9b829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Wed, 18 Nov 2020 13:08:28 +0200 Subject: [PATCH 108/202] EPP contactInfo: add test for name masking --- test/integration/epp/contact/info/base_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/epp/contact/info/base_test.rb b/test/integration/epp/contact/info/base_test.rb index 80dad97e8..4e4a9190e 100644 --- a/test/integration/epp/contact/info/base_test.rb +++ b/test/integration/epp/contact/info/base_test.rb @@ -44,7 +44,7 @@ class EppContactInfoBaseTest < EppTestCase contact: xml_schema).text end - def test_hides_password_when_current_registrar_is_not_sponsoring + def test_hides_password_and_name_when_current_registrar_is_not_sponsoring non_sponsoring_registrar = registrars(:goodnames) @contact.update!(registrar: non_sponsoring_registrar) @@ -70,6 +70,7 @@ class EppContactInfoBaseTest < EppTestCase assert_epp_response :completed_successfully response_xml = Nokogiri::XML(response.body) assert_nil response_xml.at_xpath('//contact:authInfo', contact: xml_schema) + assert_equal 'No access', response_xml.at_xpath('//contact:name', contact: xml_schema).text end private From 3d01cbb6aab4b59d065583e6fee0683dba6b6878 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 19 Nov 2020 13:40:49 +0500 Subject: [PATCH 109/202] Add test to be sure if emails are sent --- test/models/domain/force_delete_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 2c264d916..7c205fa74 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -1,18 +1,20 @@ require 'test_helper' -class NewDomainForceDeleteTest < ActiveSupport::TestCase +class ForceDeleteTest < ActionMailer::TestCase setup do @domain = domains(:shop) Setting.redemption_grace_period = 30 + ActionMailer::Base.deliveries.clear end def test_schedules_force_delete_fast_track assert_not @domain.force_delete_scheduled? travel_to Time.zone.parse('2010-07-05') - @domain.schedule_force_delete(type: :fast_track) + @domain.schedule_force_delete(type: :fast_track, notify_by_email: true) @domain.reload + assert_emails 1 assert @domain.force_delete_scheduled? assert_equal Date.parse('2010-08-20'), @domain.force_delete_date.to_date assert_equal Date.parse('2010-07-06'), @domain.force_delete_start.to_date From ef5c4bd54ebcdcab6f6544cf5b490494126c4110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 19 Nov 2020 16:15:23 +0200 Subject: [PATCH 110/202] Return total domain count in domains query --- app/controllers/api/v1/registrant/domains_controller.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/registrant/domains_controller.rb b/app/controllers/api/v1/registrant/domains_controller.rb index b985ef390..48cb95cf2 100644 --- a/app/controllers/api/v1/registrant/domains_controller.rb +++ b/app/controllers/api/v1/registrant/domains_controller.rb @@ -18,14 +18,13 @@ module Api status: :bad_request) && return end - @domains = current_user_domains.limit(limit).offset(offset) - - serialized_domains = @domains.map do |item| + domains = current_user_domains + serialized_domains = domains.limit(limit).offset(offset).map do |item| serializer = Serializers::RegistrantApi::Domain.new(item) serializer.to_json end - render json: serialized_domains + render json: {count: domains.count, domains: serialized_domains} end def show From 556008debee12987dfc31edabc4be62a4566cb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 19 Nov 2020 16:18:25 +0200 Subject: [PATCH 111/202] Fix CC issues --- app/controllers/api/v1/registrant/domains_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/registrant/domains_controller.rb b/app/controllers/api/v1/registrant/domains_controller.rb index 48cb95cf2..26f9f813b 100644 --- a/app/controllers/api/v1/registrant/domains_controller.rb +++ b/app/controllers/api/v1/registrant/domains_controller.rb @@ -24,7 +24,7 @@ module Api serializer.to_json end - render json: {count: domains.count, domains: serialized_domains} + render json: { count: domains.count, domains: serialized_domains } end def show From 0dea44dcd3c77a300f122950c0760313d2cf821e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 16:55:23 +0200 Subject: [PATCH 112/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ae259b2..e2cf1cb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +19.11.2020 +* Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) + 13.11.2020 * Fixed per registrar epp session limit [#729](https://github.com/internetee/registry/issues/729) * Correct error code is returned on reaching session limit [#587](https://github.com/internetee/registry/issues/587) From 3c3eaa960483e8acbeffa19883f65b7f59d37345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:21:18 +0200 Subject: [PATCH 113/202] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2cf1cb33..913d3cc21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) +* Refactor ForceDelete [#1740](https://github.com/internetee/registry/issues/1740) 13.11.2020 * Fixed per registrar epp session limit [#729](https://github.com/internetee/registry/issues/729) From 86b86491de42fba2078ef993a1681eb9aa45d8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:23:22 +0200 Subject: [PATCH 114/202] Update expired_soft.text.erb --- app/views/mailers/domain_expire_mailer/expired_soft.text.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.text.erb b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb index 0e6d9c953..7be32f73b 100644 --- a/app/views/mailers/domain_expire_mailer/expired_soft.text.erb +++ b/app/views/mailers/domain_expire_mailer/expired_soft.text.erb @@ -2,7 +2,7 @@ Domeen <%= @domain.name %> on aegunud ning suunatud kustutusmenetlusse kuna olem Lugupeetud .ee domeeni registreerija/halduskontakt -Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole. +Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulikke kontakti objekte, millest tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole. <%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit https://www.internet.ee/domeenioksjonid. From a5b06a7da7218aa8facec448b9d6d65d7c7b7d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 19 Nov 2020 18:24:03 +0200 Subject: [PATCH 115/202] Update expired_soft.html.erb --- app/views/mailers/domain_expire_mailer/expired_soft.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/mailers/domain_expire_mailer/expired_soft.html.erb b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb index 1dd661a38..0bcfc6acd 100644 --- a/app/views/mailers/domain_expire_mailer/expired_soft.html.erb +++ b/app/views/mailers/domain_expire_mailer/expired_soft.html.erb @@ -2,7 +2,7 @@

Lugupeetud .ee domeeni registreerija/halduskontakt

-

Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulike kontakti objekte, milles tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole.

+

Domeeninimi <%= @domain.name %> on aegunud ja ei ole alates <%= @domain.on_hold_date %> internetis kättesaadav. Domeeniga on seotud puudulikke kontakti objekte, millest tulenevalt on Eesti Interneti SA blokeerinud domeeni pikendamise ja registripidaja vahetuse, kuniks kontaktandmed korrastatakse. Andmete korrastamiseks ja registreeringu pikendamiseks pöörduge palun oma registripidaja poole.

<%= @domain.name %> pikendamata jätmisel domeen kustub ja läheb <%= @domain.delete_date %> oksjonile .ee oksjonikeskkonda. Domeenioksjonite kohta loe lähemalt siit.

From 6a6265e3a5b97918558ae4218119061ce665679c Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 18 Nov 2020 18:09:31 +0500 Subject: [PATCH 116/202] Move CancelForceDelete to interactor --- .../cancel_force_delete_interaction/base.rb | 7 +++++ .../cancel_force_delete.rb | 10 +++++++ .../clear_force_delete_data.rb | 10 +++++++ .../notify_registrar.rb | 8 ++++++ .../remove_force_delete_statuses.rb | 11 ++++++++ .../restore_statuses_before_force_delete.rb | 9 +++++++ app/models/concerns/domain/force_delete.rb | 26 +------------------ 7 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 app/interactions/cancel_force_delete_interaction/base.rb create mode 100644 app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb create mode 100644 app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb create mode 100644 app/interactions/cancel_force_delete_interaction/notify_registrar.rb create mode 100644 app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb create mode 100644 app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb diff --git a/app/interactions/cancel_force_delete_interaction/base.rb b/app/interactions/cancel_force_delete_interaction/base.rb new file mode 100644 index 000000000..595279d87 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/base.rb @@ -0,0 +1,7 @@ +module CancelForceDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to cancel ForceDelete on' + end +end diff --git a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb new file mode 100644 index 000000000..2f45bf0e4 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb @@ -0,0 +1,10 @@ +module CancelForceDeleteInteraction + class CancelForceDelete < Base + def execute + compose(RemoveForceDeleteStatuses, inputs) + compose(RestoreStatusesBeforeForceDelete, inputs) + compose(ClearForceDeleteData, inputs) + compose(NotifyRegistrar, inputs) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb new file mode 100644 index 000000000..ccc0714f7 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb @@ -0,0 +1,10 @@ +module CancelForceDeleteInteraction + class ClearForceDeleteData < Base + def execute + domain.force_delete_data = nil + domain.force_delete_date = nil + domain.force_delete_start = nil + domain.save(validate: false) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb new file mode 100644 index 000000000..3ded4c489 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb @@ -0,0 +1,8 @@ +module CancelForceDeleteInteraction + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', + domain_name: domain.name)) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb new file mode 100644 index 000000000..1cc4989e5 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb @@ -0,0 +1,11 @@ +module CancelForceDeleteInteraction + class RemoveForceDeleteStatuses < Base + def execute + domain.statuses.delete(DomainStatus::FORCE_DELETE) + domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) + domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) + domain.statuses.delete(DomainStatus::CLIENT_HOLD) + domain.save(validate: false) + end + end +end diff --git a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb new file mode 100644 index 000000000..06b6bbd18 --- /dev/null +++ b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb @@ -0,0 +1,9 @@ +module CancelForceDeleteInteraction + class RestoreStatusesBeforeForceDelete < Base + def execute + domain.statuses = domain.statuses_before_force_delete + domain.statuses_before_force_delete = nil + domain.save(validate: false) + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index b119b0ce0..ff869fc0a 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -58,18 +58,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength notify_by_email: notify_by_email) end - def clear_force_delete_data - self.force_delete_data = nil - end - def cancel_force_delete - remove_force_delete_statuses - restore_statuses_before_force_delete - clear_force_delete_data - self.force_delete_date = nil - self.force_delete_start = nil - save(validate: false) - registrar.notifications.create!(text: I18n.t('force_delete_cancelled', domain_name: name)) + CancelForceDeleteInteraction::CancelForceDelete.run(domain: self) end def outzone_date @@ -80,18 +70,4 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength (force_delete_date&.beginning_of_day || valid_to + Setting.expire_warning_period.days + Setting.redemption_grace_period.days) end - - private - - def restore_statuses_before_force_delete - self.statuses = statuses_before_force_delete - self.statuses_before_force_delete = nil - end - - def remove_force_delete_statuses - statuses.delete(DomainStatus::FORCE_DELETE) - statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) - statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) - statuses.delete(DomainStatus::CLIENT_HOLD) - end end From 822f19a676f02561419aa5e0d99dc5b7de794a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 20 Nov 2020 13:48:07 +0200 Subject: [PATCH 117/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 913d3cc21..52408c420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +20.11.2020 +* Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) + 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) * Refactor ForceDelete [#1740](https://github.com/internetee/registry/issues/1740) From fce16f924d02e5f6cd89c0845efd2794e6db65f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 20 Nov 2020 17:28:40 +0200 Subject: [PATCH 118/202] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52408c420..28810c8d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) +* Refactored forceDelete cancellation for interactors [#1743](https://github.com/internetee/registry/issues/1743) 19.11.2020 * Only sponsoring registrar has access to private contact's details [#1745](https://github.com/internetee/registry/issues/1745) From a40116e05e5e80378ef99f861ec824c020ffd276 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 20 Nov 2020 16:51:05 +0500 Subject: [PATCH 119/202] Move ClientHold procedure to interactor Closes #1751 --- .../client_hold_interaction/base.rb | 8 +++ .../process_client_hold.rb | 67 +++++++++++++++++++ .../set_client_hold.rb | 15 +++++ app/models/concerns/domain/force_delete.rb | 19 ------ app/models/concerns/job/force_delete.rb | 34 ---------- .../concerns/job/force_delete_logging.rb | 34 ---------- .../concerns/job/force_delete_notify.rb | 31 --------- app/models/domain_cron.rb | 8 +-- test/models/domain/force_delete_test.rb | 13 ++-- 9 files changed, 102 insertions(+), 127 deletions(-) create mode 100644 app/interactions/client_hold_interaction/base.rb create mode 100644 app/interactions/client_hold_interaction/process_client_hold.rb create mode 100644 app/interactions/client_hold_interaction/set_client_hold.rb delete mode 100644 app/models/concerns/job/force_delete.rb delete mode 100644 app/models/concerns/job/force_delete_logging.rb delete mode 100644 app/models/concerns/job/force_delete_notify.rb diff --git a/app/interactions/client_hold_interaction/base.rb b/app/interactions/client_hold_interaction/base.rb new file mode 100644 index 000000000..be8b8b0a8 --- /dev/null +++ b/app/interactions/client_hold_interaction/base.rb @@ -0,0 +1,8 @@ +module ClientHoldInteraction + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end +end diff --git a/app/interactions/client_hold_interaction/process_client_hold.rb b/app/interactions/client_hold_interaction/process_client_hold.rb new file mode 100644 index 000000000..453c400cc --- /dev/null +++ b/app/interactions/client_hold_interaction/process_client_hold.rb @@ -0,0 +1,67 @@ +module ClientHoldInteraction + class ProcessClientHold < Base + object :domain, + class: Domain, + description: 'Domain to set ClientHold on' + + # rubocop:disable Metrics/AbcSize + def execute + notify_on_grace_period if should_notify_on_soft_force_delete? + + return unless client_holdable? + + domain.statuses << DomainStatus::CLIENT_HOLD + to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") + + domain.save(validate: false) + notify_client_hold + + to_stdout("Successfully set client_hold on (#{domain.name})") + end + + def notify_on_grace_period + domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', + domain_name: domain.name, + date: domain.force_delete_start)) + send_mail if domain.template_name.present? + domain.update(contact_notification_sent_date: Time.zone.today) + end + + def notify_client_hold + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + + def send_mail + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.template_name).deliver_now + end + + def should_notify_on_soft_force_delete? + domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && + domain.force_delete_start.to_date <= Time.zone.now.to_date && + domain.force_delete_type.to_sym == :soft && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) + end + # rubocop:enable Metrics/AbcSize + + def client_holdable? + domain.force_delete_scheduled? && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && + domain.force_delete_start.present? && + force_delete_lte_today && force_delete_lte_valid_date + end + + def force_delete_lte_today + domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now + end + + def force_delete_lte_valid_date + domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to + end + end +end diff --git a/app/interactions/client_hold_interaction/set_client_hold.rb b/app/interactions/client_hold_interaction/set_client_hold.rb new file mode 100644 index 000000000..d723d5cc8 --- /dev/null +++ b/app/interactions/client_hold_interaction/set_client_hold.rb @@ -0,0 +1,15 @@ +module ClientHoldInteraction + class SetClientHold < Base + def execute + to_stdout('Setting client_hold to domains\n') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + ::Domain.force_delete_scheduled.each do |domain| + ClientHoldInteraction::ProcessClientHold.run(domain: domain) + end + + to_stdout('All client_hold setting are done\n') + end + end +end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index ff869fc0a..988c8075a 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -33,25 +33,6 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength statuses.include?(DomainStatus::FORCE_DELETE) end - def should_notify_on_soft_force_delete? - force_delete_scheduled? && contact_notification_sent_date.blank? && - force_delete_start.to_date <= Time.zone.now.to_date && force_delete_type.to_sym == :soft && - !statuses.include?(DomainStatus::CLIENT_HOLD) - end - - def client_holdable? - force_delete_scheduled? && !statuses.include?(DomainStatus::CLIENT_HOLD) && - force_delete_start.present? && force_delete_lte_today && force_delete_lte_valid_date - end - - def force_delete_lte_today - force_delete_start + Setting.expire_warning_period.days <= Time.zone.now - end - - def force_delete_lte_valid_date - force_delete_start + Setting.expire_warning_period.days <= valid_to - end - def schedule_force_delete(type: :fast_track, notify_by_email: false) ForceDeleteInteraction::SetForceDelete.run(domain: self, type: type, diff --git a/app/models/concerns/job/force_delete.rb b/app/models/concerns/job/force_delete.rb deleted file mode 100644 index 316612d1e..000000000 --- a/app/models/concerns/job/force_delete.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Concerns - module Job - module ForceDelete - extend ActiveSupport::Concern - - class_methods do - def start_client_hold - log_prepare_client_hold - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - - ::Domain.force_delete_scheduled.each do |domain| - proceed_client_hold(domain: domain) - end - - log_end_end_force_delete_job - end - - def proceed_client_hold(domain:) - notify_on_grace_period(domain) if domain.should_notify_on_soft_force_delete? - return unless domain.client_holdable? - - domain.statuses << DomainStatus::CLIENT_HOLD - log_start_client_hold(domain) - - domain.save(validate: false) - notify_client_hold(domain) - - log_end_end_client_hold(domain) - end - end - end - end -end diff --git a/app/models/concerns/job/force_delete_logging.rb b/app/models/concerns/job/force_delete_logging.rb deleted file mode 100644 index 8f6ee227c..000000000 --- a/app/models/concerns/job/force_delete_logging.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Concerns - module Job - module ForceDeleteLogging - extend ActiveSupport::Concern - - class_methods do - def log_prepare_client_hold - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - Setting client_hold to domains\n" - end - - def log_start_client_hold(domain) - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} DomainCron.start_client_hold: ##{domain.id} "\ - "(#{domain.name}) #{domain.changes}\n" - end - - def log_end_end_client_hold(domain) - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - Successfully set client_hold on (#{domain.name})" - end - - def log_end_end_force_delete_job - return if Rails.env.test? - - STDOUT << "#{Time.zone.now.utc} - All client_hold setting are done\n" - end - end - end - end -end diff --git a/app/models/concerns/job/force_delete_notify.rb b/app/models/concerns/job/force_delete_notify.rb deleted file mode 100644 index bc291354e..000000000 --- a/app/models/concerns/job/force_delete_notify.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Concerns - module Job - module ForceDeleteNotify - extend ActiveSupport::Concern - - class_methods do - def notify_client_hold(domain) - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - - def notify_on_grace_period(domain) - domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', - domain_name: domain.name, - date: domain.force_delete_start)) - send_mail(domain) if domain.template_name.present? - domain.update(contact_notification_sent_date: Time.zone.today) - end - - def send_mail(domain) - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.template_name).deliver_now - end - end - end - end -end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index ad64456ca..613762062 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -1,8 +1,4 @@ class DomainCron - include Concerns::Job::ForceDelete - include Concerns::Job::ForceDeleteLogging - include Concerns::Job::ForceDeleteNotify - def self.clean_expired_pendings STDOUT << "#{Time.zone.now.utc} - Clean expired domain pendings\n" unless Rails.env.test? @@ -81,4 +77,8 @@ class DomainCron STDOUT << "#{Time.zone.now.utc} - Successfully set server_hold to #{marked} of #{real} domains\n" unless Rails.env.test? marked end + + def self.start_client_hold + ClientHoldInteraction::SetClientHold.run! + end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 7c205fa74..d9e7a3632 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -206,9 +206,10 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload + assert_emails 1 assert_equal(@domain.purge_date.to_date, @domain.force_delete_date.to_date) assert_equal(@domain.outzone_date.to_date, @domain.force_delete_start.to_date + Setting.expire_warning_period.days) @@ -226,8 +227,10 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload + + assert_emails 1 assert_includes(@domain.statuses, asserted_status) end @@ -241,7 +244,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-07-06') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) @@ -256,7 +259,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-25') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_includes(@domain.statuses, asserted_status) @@ -272,7 +275,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-06') - DomainCron.start_client_hold + ClientHoldInteraction::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) From a3e6bc4cf90c319a797a3b19cf6188d8360628ff Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:31:26 +0500 Subject: [PATCH 120/202] Mode DomainDeleteConfirm job to interactor --- .../send_request.rb | 22 +++++++++++++++++++ app/jobs/domain_delete_confirm_email_job.rb | 18 +-------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 app/interactions/domain_delete_confirm_interaction/send_request.rb diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb new file mode 100644 index 000000000..6e734d40d --- /dev/null +++ b/app/interactions/domain_delete_confirm_interaction/send_request.rb @@ -0,0 +1,22 @@ +module DomainDeleteConfirmInteraction + class SendRequest < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to send delete confirmation' + + def execute + log + DomainDeleteMailer.confirmation_request(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_now + end + + private + + def log + message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ + " to #{domain.registrant.email}" + Rails.logger.info(message) + end + end +end diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb index ea5a9bf49..ef9a1ba96 100644 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ b/app/jobs/domain_delete_confirm_email_job.rb @@ -1,22 +1,6 @@ class DomainDeleteConfirmEmailJob < Que::Job def run(domain_id) domain = Domain.find(domain_id) - - log(domain) - DomainDeleteMailer.confirmation_request(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_now - end - - private - - def log(domain) - message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ - " to #{domain.registrant.email}" - logger.info(message) - end - - def logger - Rails.logger + DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) end end From cb3cf37331a090d6ab03ef82afdf3bfa228e4568 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:57:28 +0500 Subject: [PATCH 121/202] Change email sending from job to delayed send --- .../send_request.rb | 2 +- app/jobs/domain_delete_confirm_email_job.rb | 8 ++++---- app/models/domain.rb | 2 +- test/integration/epp/domain/delete/base_test.rb | 13 +++++++++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb index 6e734d40d..bbf4c8d9e 100644 --- a/app/interactions/domain_delete_confirm_interaction/send_request.rb +++ b/app/interactions/domain_delete_confirm_interaction/send_request.rb @@ -8,7 +8,7 @@ module DomainDeleteConfirmInteraction log DomainDeleteMailer.confirmation_request(domain: domain, registrar: domain.registrar, - registrant: domain.registrant).deliver_now + registrant: domain.registrant).deliver_later end private diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb index ef9a1ba96..d2c814bd8 100644 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ b/app/jobs/domain_delete_confirm_email_job.rb @@ -1,6 +1,6 @@ class DomainDeleteConfirmEmailJob < Que::Job - def run(domain_id) - domain = Domain.find(domain_id) - DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) - end + # def run(domain_id) + # domain = Domain.find(domain_id) + # DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) + # end end diff --git a/app/models/domain.rb b/app/models/domain.rb index b15bb7c55..1ef66ffab 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -418,7 +418,7 @@ class Domain < ApplicationRecord pending_delete_confirmation! save(validate: false) # should check if this did succeed - DomainDeleteConfirmEmailJob.enqueue(id) + DomainDeleteConfirmInteraction::SendRequest.run(domain: self) end def cancel_pending_delete diff --git a/test/integration/epp/domain/delete/base_test.rb b/test/integration/epp/domain/delete/base_test.rb index bfdfa9f75..56a3cc31e 100644 --- a/test/integration/epp/domain/delete/base_test.rb +++ b/test/integration/epp/domain/delete/base_test.rb @@ -35,7 +35,6 @@ class EppDomainDeleteBaseTest < EppTestCase XML post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } - # binding.pry assert_includes Domain.find_by(name: 'invalid.test').statuses, DomainStatus::PENDING_DELETE_CONFIRMATION assert_epp_response :completed_successfully_action_pending end @@ -90,7 +89,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert @domain.registrant_verification_asked? @@ -121,7 +122,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert_not @domain.registrant_verification_asked? @@ -152,7 +155,9 @@ class EppDomainDeleteBaseTest < EppTestCase XML - post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + perform_enqueued_jobs do + post epp_delete_path, params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + end @domain.reload assert_not @domain.registrant_verification_asked? From 5a82d7c2c01cacdc91d30ec6ff7f62cacaf10809 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 16:36:30 +0500 Subject: [PATCH 122/202] Fix error handling in processing request IP --- app/models/white_ip.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index c2fb51cd1..a5041095e 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,10 +41,15 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4 + ipv6).pluck(:id).flatten.uniq + new_ip4 = IPAddr.new(ip, Socket::AF_INET) + new_ip6 = IPAddr.new(ip, Socket::AF_INET6) + + result = self.all.select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === new_ip4 || + IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === new_ip6 } + ids = result.pluck(:id).flatten.uniq where(id: ids).any? + rescue IPAddr::InvalidAddressError => _e + false end # rubocop:enable Style/CaseEquality end From bace495f575a73a0b55e2542853c31f8f746b707 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 17:24:52 +0500 Subject: [PATCH 123/202] Revert "Fix error handling in processing request IP" This reverts commit 5a82d7c2c01cacdc91d30ec6ff7f62cacaf10809. --- app/models/white_ip.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index a5041095e..c2fb51cd1 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,15 +41,10 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - new_ip4 = IPAddr.new(ip, Socket::AF_INET) - new_ip6 = IPAddr.new(ip, Socket::AF_INET6) - - result = self.all.select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === new_ip4 || - IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === new_ip6 } - ids = result.pluck(:id).flatten.uniq + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } + ids = (ipv4 + ipv6).pluck(:id).flatten.uniq where(id: ids).any? - rescue IPAddr::InvalidAddressError => _e - false end # rubocop:enable Style/CaseEquality end From f01e59d95aaa965664216a9bda4346894cd205d9 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 17:29:59 +0500 Subject: [PATCH 124/202] Some white ip check fixes --- app/models/white_ip.rb | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index c2fb51cd1..7198c225f 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,11 +41,35 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === IPAddr.new(ip) } - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === IPAddr.new(ip) } - ids = (ipv4 + ipv6).pluck(:id).flatten.uniq - where(id: ids).any? + Rails.logger.info "Checking if whitelist includes ip:#{ip}" + return false if ip.blank? + + where(id: ids_including(ip)).any? + end + + def ids_including(ip) + ipv4 = ipv6 = [] + if check_ip4(ip).present? + ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } + end + if check_ip6(ip).present? + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6 } + end + (ipv4 + ipv6).pluck(:id).flatten.uniq end # rubocop:enable Style/CaseEquality + + def check_ip4(ip) + IPAddr.new(ip, Socket::AF_INET) + rescue StandardError => _e + nil + end + + def check_ip6(ip) + IPAddr.new(ip, Socket::AF_INET6) + rescue StandardError => _e + nil + end + end end From 3943628716d97f4d043c3ffb35dece36b903fabe Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 23 Nov 2020 18:07:01 +0500 Subject: [PATCH 125/202] Add some debug messages --- app/models/authorization/restricted_ip.rb | 2 ++ app/models/registrar.rb | 3 ++- app/models/white_ip.rb | 13 +++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/authorization/restricted_ip.rb b/app/models/authorization/restricted_ip.rb index b3c7b7cdb..0149814d5 100644 --- a/app/models/authorization/restricted_ip.rb +++ b/app/models/authorization/restricted_ip.rb @@ -14,7 +14,9 @@ module Authorization end def can_access_registrar_area_sign_in_page? + Rails.logger.info "Checking if Auth::RestrictedIp.enabled: #{self.class.enabled?}" return true unless self.class.enabled? + Rails.logger.info "Checking if registrar area accessible, result: #{WhiteIp.registrar_area.include_ip?(ip)}" WhiteIp.registrar_area.include_ip?(ip) end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 040c7886b..4503bedd0 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -137,7 +137,8 @@ class Registrar < ApplicationRecord def api_ip_white?(ip) return true unless Setting.api_ip_whitelist_enabled - white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + # white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + white_ips.api.include_ip?(ip) end # Audit log is needed, therefore no raw SQL diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 7198c225f..1d3379de5 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -41,21 +41,26 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality def include_ip?(ip) - Rails.logger.info "Checking if whitelist includes ip:#{ip}" + Rails.logger.info "Checking if whitelist includes ip: #{ip}" return false if ip.blank? - where(id: ids_including(ip)).any? + result = where(id: ids_including(ip)).any? + Rails.logger.info "Result is: #{result}" + result end def ids_including(ip) + Rails.logger.info "Checking ip #{ip}" ipv4 = ipv6 = [] if check_ip4(ip).present? ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } end if check_ip6(ip).present? - ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6 } + ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) } end - (ipv4 + ipv6).pluck(:id).flatten.uniq + result = (ipv4 + ipv6).pluck(:id).flatten.uniq + Rails.logger.info "Result is #{result}" + result end # rubocop:enable Style/CaseEquality From 708e87f5a7946488bac621b5f4381fbefc5d1322 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 24 Nov 2020 13:19:08 +0500 Subject: [PATCH 126/202] CC fixes --- app/models/authorization/restricted_ip.rb | 2 -- app/models/registrar.rb | 2 +- app/models/white_ip.rb | 13 ++++--------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/app/models/authorization/restricted_ip.rb b/app/models/authorization/restricted_ip.rb index 0149814d5..b3c7b7cdb 100644 --- a/app/models/authorization/restricted_ip.rb +++ b/app/models/authorization/restricted_ip.rb @@ -14,9 +14,7 @@ module Authorization end def can_access_registrar_area_sign_in_page? - Rails.logger.info "Checking if Auth::RestrictedIp.enabled: #{self.class.enabled?}" return true unless self.class.enabled? - Rails.logger.info "Checking if registrar area accessible, result: #{WhiteIp.registrar_area.include_ip?(ip)}" WhiteIp.registrar_area.include_ip?(ip) end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 4503bedd0..149bb7541 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -137,7 +137,7 @@ class Registrar < ApplicationRecord def api_ip_white?(ip) return true unless Setting.api_ip_whitelist_enabled - # white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) + white_ips.api.include_ip?(ip) end diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 1d3379de5..417633b12 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -40,17 +40,14 @@ class WhiteIp < ApplicationRecord class << self # rubocop:disable Style/CaseEquality + # rubocop:disable Metrics/AbcSize def include_ip?(ip) - Rails.logger.info "Checking if whitelist includes ip: #{ip}" return false if ip.blank? - result = where(id: ids_including(ip)).any? - Rails.logger.info "Result is: #{result}" - result + where(id: ids_including(ip)).any? end def ids_including(ip) - Rails.logger.info "Checking ip #{ip}" ipv4 = ipv6 = [] if check_ip4(ip).present? ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) } @@ -58,11 +55,10 @@ class WhiteIp < ApplicationRecord if check_ip6(ip).present? ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) } end - result = (ipv4 + ipv6).pluck(:id).flatten.uniq - Rails.logger.info "Result is #{result}" - result + (ipv4 + ipv6).pluck(:id).flatten.uniq end # rubocop:enable Style/CaseEquality + # rubocop:enable Metrics/AbcSize def check_ip4(ip) IPAddr.new(ip, Socket::AF_INET) @@ -75,6 +71,5 @@ class WhiteIp < ApplicationRecord rescue StandardError => _e nil end - end end From 89c5413fc702c8399474ff18f6f245af531deeba Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 24 Nov 2020 13:26:38 +0500 Subject: [PATCH 127/202] Unused class clean-up --- app/jobs/domain_delete_confirm_email_job.rb | 6 ------ test/models/whois/record_test.rb | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 app/jobs/domain_delete_confirm_email_job.rb diff --git a/app/jobs/domain_delete_confirm_email_job.rb b/app/jobs/domain_delete_confirm_email_job.rb deleted file mode 100644 index d2c814bd8..000000000 --- a/app/jobs/domain_delete_confirm_email_job.rb +++ /dev/null @@ -1,6 +0,0 @@ -class DomainDeleteConfirmEmailJob < Que::Job - # def run(domain_id) - # domain = Domain.find(domain_id) - # DomainDeleteConfirmInteraction::SendRequest.run(domain: domain) - # end -end diff --git a/test/models/whois/record_test.rb b/test/models/whois/record_test.rb index e900a4965..d06b23cae 100644 --- a/test/models/whois/record_test.rb +++ b/test/models/whois/record_test.rb @@ -70,6 +70,6 @@ class Whois::RecordTest < ActiveSupport::TestCase end def registration_deadline - Time.zone.now + 10.days + @registration_deadline ||= Time.zone.now + 10.days end end From 283bf8cd1adaf6d8a6ed13aac1c980d210769159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 24 Nov 2020 14:25:52 +0200 Subject: [PATCH 128/202] Fix contact version view --- app/helpers/object_versions_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb index dccf03017..be8ef1217 100644 --- a/app/helpers/object_versions_helper.rb +++ b/app/helpers/object_versions_helper.rb @@ -3,7 +3,7 @@ module ObjectVersionsHelper version.object_changes.to_h.each do |key, value| method_name = "#{key}=".to_sym if new_object.respond_to?(method_name) - new_object.public_send(method_name, event_value(event, value)) + new_object.public_send(method_name, event_value(version, value)) end end end From ee92807139cc5f5c0c5ee20531a5ab84b41991c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 24 Nov 2020 15:03:06 +0200 Subject: [PATCH 129/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28810c8d8..22b3d7d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +24.11.2020 +* Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) + 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) * Refactored forceDelete cancellation for interactors [#1743](https://github.com/internetee/registry/issues/1743) From b39de0885a4dd88ce95d111da6196705df2db252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 24 Nov 2020 15:29:50 +0200 Subject: [PATCH 130/202] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b3d7d02..ebbc3d905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 24.11.2020 * Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) +* Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580) 20.11.2020 * Registrant confirmation over Registrant API [#1742](https://github.com/internetee/registry/pull/1742) From 09a58fa432193dee065ebcbc0556cc96e877e0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Fri, 27 Nov 2020 18:12:49 +0200 Subject: [PATCH 131/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbc3d905..44329c4d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +27.11.2020 +* Refactored delete confirmation for interactors [#1753](https://github.com/internetee/registry/issues/1753) + 24.11.2020 * Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983) * Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580) From 7b3361394168656ce305cbbc0e885555fa5e712f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 10:31:46 +0200 Subject: [PATCH 132/202] Reduce domains list API call --- .../api/v1/registrant/domains_controller.rb | 4 ++-- lib/serializers/registrant_api/domain.rb | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/registrant/domains_controller.rb b/app/controllers/api/v1/registrant/domains_controller.rb index 26f9f813b..390148488 100644 --- a/app/controllers/api/v1/registrant/domains_controller.rb +++ b/app/controllers/api/v1/registrant/domains_controller.rb @@ -20,7 +20,7 @@ module Api domains = current_user_domains serialized_domains = domains.limit(limit).offset(offset).map do |item| - serializer = Serializers::RegistrantApi::Domain.new(item) + serializer = Serializers::RegistrantApi::Domain.new(item, simplify: true) serializer.to_json end @@ -31,7 +31,7 @@ module Api @domain = current_user_domains.find_by(uuid: params[:uuid]) if @domain - serializer = Serializers::RegistrantApi::Domain.new(@domain) + serializer = Serializers::RegistrantApi::Domain.new(@domain, simplify: false) render json: serializer.to_json else render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found diff --git a/lib/serializers/registrant_api/domain.rb b/lib/serializers/registrant_api/domain.rb index 542f2d0de..f0c9c1876 100644 --- a/lib/serializers/registrant_api/domain.rb +++ b/lib/serializers/registrant_api/domain.rb @@ -3,11 +3,27 @@ module Serializers class Domain attr_reader :domain - def initialize(domain) + def initialize(domain, simplify: false) @domain = domain + @simplify = simplify end def to_json + if simplify + return { + id: domain.uuid, + name: domain.name, + registered_at: domain.registered_at, + valid_to: domain.valid_to, + registrant_verification_asked_at: domain.registrant_verification_asked_at, + statuses: domain.statuses, + registrar: { + name: domain.registrar.name, + website: domain.registrar.website + } + } + end + { id: domain.uuid, name: domain.name, From 5363c546a598ecfe8460a43c6bb2c105ea764ab0 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 13:46:53 +0500 Subject: [PATCH 133/202] Move all the existing interactors to Domains namespace --- .../cancel_force_delete_interaction/base.rb | 7 ---- .../cancel_force_delete.rb | 10 ----- .../clear_force_delete_data.rb | 10 ----- .../notify_registrar.rb | 8 ---- .../remove_force_delete_statuses.rb | 11 ----- .../restore_statuses_before_force_delete.rb | 9 ----- .../send_request.rb | 22 ---------- .../domains/cancel_force_delete/base.rb | 9 +++++ .../cancel_force_delete.rb | 12 ++++++ .../clear_force_delete_data.rb | 12 ++++++ .../cancel_force_delete/notify_registrar.rb | 10 +++++ .../remove_force_delete_statuses.rb | 13 ++++++ .../restore_statuses_before_force_delete.rb | 11 +++++ .../domains/delete_confirm/send_request.rb | 24 +++++++++++ app/interactions/domains/force_delete/base.rb | 17 ++++++++ .../domains/force_delete/check_discarded.rb | 12 ++++++ .../domains/force_delete/notify_by_email.rb | 23 +++++++++++ .../domains/force_delete/notify_registrar.rb | 12 ++++++ .../domains/force_delete/post_set_process.rb | 19 +++++++++ .../domains/force_delete/prepare_domain.rb | 15 +++++++ .../domains/force_delete/set_force_delete.rb | 14 +++++++ .../domains/force_delete/set_status.rb | 40 +++++++++++++++++++ .../force_delete_interaction/base.rb | 16 -------- .../check_discarded.rb | 11 ----- .../notify_by_email.rb | 21 ---------- .../notify_registrar.rb | 10 ----- .../post_set_process.rb | 17 -------- .../prepare_domain.rb | 13 ------ .../set_force_delete.rb | 12 ------ .../force_delete_interaction/set_status.rb | 38 ------------------ app/models/concerns/domain/force_delete.rb | 8 ++-- app/models/domain.rb | 2 +- test/models/domain/force_delete_test.rb | 2 +- test/models/domain_test.rb | 2 +- 34 files changed, 250 insertions(+), 222 deletions(-) delete mode 100644 app/interactions/cancel_force_delete_interaction/base.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb delete mode 100644 app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb delete mode 100644 app/interactions/domain_delete_confirm_interaction/send_request.rb create mode 100644 app/interactions/domains/cancel_force_delete/base.rb create mode 100644 app/interactions/domains/cancel_force_delete/cancel_force_delete.rb create mode 100644 app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb create mode 100644 app/interactions/domains/cancel_force_delete/notify_registrar.rb create mode 100644 app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb create mode 100644 app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb create mode 100644 app/interactions/domains/delete_confirm/send_request.rb create mode 100644 app/interactions/domains/force_delete/base.rb create mode 100644 app/interactions/domains/force_delete/check_discarded.rb create mode 100644 app/interactions/domains/force_delete/notify_by_email.rb create mode 100644 app/interactions/domains/force_delete/notify_registrar.rb create mode 100644 app/interactions/domains/force_delete/post_set_process.rb create mode 100644 app/interactions/domains/force_delete/prepare_domain.rb create mode 100644 app/interactions/domains/force_delete/set_force_delete.rb create mode 100644 app/interactions/domains/force_delete/set_status.rb delete mode 100644 app/interactions/force_delete_interaction/base.rb delete mode 100644 app/interactions/force_delete_interaction/check_discarded.rb delete mode 100644 app/interactions/force_delete_interaction/notify_by_email.rb delete mode 100644 app/interactions/force_delete_interaction/notify_registrar.rb delete mode 100644 app/interactions/force_delete_interaction/post_set_process.rb delete mode 100644 app/interactions/force_delete_interaction/prepare_domain.rb delete mode 100644 app/interactions/force_delete_interaction/set_force_delete.rb delete mode 100644 app/interactions/force_delete_interaction/set_status.rb diff --git a/app/interactions/cancel_force_delete_interaction/base.rb b/app/interactions/cancel_force_delete_interaction/base.rb deleted file mode 100644 index 595279d87..000000000 --- a/app/interactions/cancel_force_delete_interaction/base.rb +++ /dev/null @@ -1,7 +0,0 @@ -module CancelForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to cancel ForceDelete on' - end -end diff --git a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb b/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb deleted file mode 100644 index 2f45bf0e4..000000000 --- a/app/interactions/cancel_force_delete_interaction/cancel_force_delete.rb +++ /dev/null @@ -1,10 +0,0 @@ -module CancelForceDeleteInteraction - class CancelForceDelete < Base - def execute - compose(RemoveForceDeleteStatuses, inputs) - compose(RestoreStatusesBeforeForceDelete, inputs) - compose(ClearForceDeleteData, inputs) - compose(NotifyRegistrar, inputs) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb b/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb deleted file mode 100644 index ccc0714f7..000000000 --- a/app/interactions/cancel_force_delete_interaction/clear_force_delete_data.rb +++ /dev/null @@ -1,10 +0,0 @@ -module CancelForceDeleteInteraction - class ClearForceDeleteData < Base - def execute - domain.force_delete_data = nil - domain.force_delete_date = nil - domain.force_delete_start = nil - domain.save(validate: false) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb b/app/interactions/cancel_force_delete_interaction/notify_registrar.rb deleted file mode 100644 index 3ded4c489..000000000 --- a/app/interactions/cancel_force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,8 +0,0 @@ -module CancelForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', - domain_name: domain.name)) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb b/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb deleted file mode 100644 index 1cc4989e5..000000000 --- a/app/interactions/cancel_force_delete_interaction/remove_force_delete_statuses.rb +++ /dev/null @@ -1,11 +0,0 @@ -module CancelForceDeleteInteraction - class RemoveForceDeleteStatuses < Base - def execute - domain.statuses.delete(DomainStatus::FORCE_DELETE) - domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) - domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) - domain.statuses.delete(DomainStatus::CLIENT_HOLD) - domain.save(validate: false) - end - end -end diff --git a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb b/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb deleted file mode 100644 index 06b6bbd18..000000000 --- a/app/interactions/cancel_force_delete_interaction/restore_statuses_before_force_delete.rb +++ /dev/null @@ -1,9 +0,0 @@ -module CancelForceDeleteInteraction - class RestoreStatusesBeforeForceDelete < Base - def execute - domain.statuses = domain.statuses_before_force_delete - domain.statuses_before_force_delete = nil - domain.save(validate: false) - end - end -end diff --git a/app/interactions/domain_delete_confirm_interaction/send_request.rb b/app/interactions/domain_delete_confirm_interaction/send_request.rb deleted file mode 100644 index bbf4c8d9e..000000000 --- a/app/interactions/domain_delete_confirm_interaction/send_request.rb +++ /dev/null @@ -1,22 +0,0 @@ -module DomainDeleteConfirmInteraction - class SendRequest < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to send delete confirmation' - - def execute - log - DomainDeleteMailer.confirmation_request(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_later - end - - private - - def log - message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \ - " to #{domain.registrant.email}" - Rails.logger.info(message) - end - end -end diff --git a/app/interactions/domains/cancel_force_delete/base.rb b/app/interactions/domains/cancel_force_delete/base.rb new file mode 100644 index 000000000..b15a3ae77 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/base.rb @@ -0,0 +1,9 @@ +module Domains + module CancelForceDelete + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to cancel ForceDelete on' + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb b/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb new file mode 100644 index 000000000..7c4ca90e1 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/cancel_force_delete.rb @@ -0,0 +1,12 @@ +module Domains + module CancelForceDelete + class CancelForceDelete < Base + def execute + compose(RemoveForceDeleteStatuses, inputs) + compose(RestoreStatusesBeforeForceDelete, inputs) + compose(ClearForceDeleteData, inputs) + compose(NotifyRegistrar, inputs) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb b/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb new file mode 100644 index 000000000..066df04fd --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/clear_force_delete_data.rb @@ -0,0 +1,12 @@ +module Domains + module CancelForceDelete + class ClearForceDeleteData < Base + def execute + domain.force_delete_data = nil + domain.force_delete_date = nil + domain.force_delete_start = nil + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/notify_registrar.rb b/app/interactions/domains/cancel_force_delete/notify_registrar.rb new file mode 100644 index 000000000..0de69e13e --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/notify_registrar.rb @@ -0,0 +1,10 @@ +module Domains + module CancelForceDelete + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled', + domain_name: domain.name)) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb b/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb new file mode 100644 index 000000000..c77820ecf --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/remove_force_delete_statuses.rb @@ -0,0 +1,13 @@ +module Domains + module CancelForceDelete + class RemoveForceDeleteStatuses < Base + def execute + domain.statuses.delete(DomainStatus::FORCE_DELETE) + domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED) + domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED) + domain.statuses.delete(DomainStatus::CLIENT_HOLD) + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb new file mode 100644 index 000000000..c55c06764 --- /dev/null +++ b/app/interactions/domains/cancel_force_delete/restore_statuses_before_force_delete.rb @@ -0,0 +1,11 @@ +module Domains + module CancelForceDelete + class RestoreStatusesBeforeForceDelete < Base + def execute + domain.statuses = domain.statuses_before_force_delete + domain.statuses_before_force_delete = nil + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/delete_confirm/send_request.rb b/app/interactions/domains/delete_confirm/send_request.rb new file mode 100644 index 000000000..91afaefb8 --- /dev/null +++ b/app/interactions/domains/delete_confirm/send_request.rb @@ -0,0 +1,24 @@ +module Domains + module DeleteConfirm + class SendRequest < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to send delete confirmation' + + def execute + log + DomainDeleteMailer.confirmation_request(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_later + end + + private + + def log + message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})"\ + " to #{domain.registrant.email}" + Rails.logger.info(message) + end + end + end +end diff --git a/app/interactions/domains/force_delete/base.rb b/app/interactions/domains/force_delete/base.rb new file mode 100644 index 000000000..27601c1d2 --- /dev/null +++ b/app/interactions/domains/force_delete/base.rb @@ -0,0 +1,17 @@ +module Domains + module ForceDelete + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to set ForceDelete on' + symbol :type, + default: :fast_track, + description: 'Force delete type, might be :fast_track or :soft' + boolean :notify_by_email, + default: false, + description: 'Do we need to send email notification' + + validates :type, inclusion: { in: %i[fast_track soft] } + end + end +end diff --git a/app/interactions/domains/force_delete/check_discarded.rb b/app/interactions/domains/force_delete/check_discarded.rb new file mode 100644 index 000000000..cbb9c1dc6 --- /dev/null +++ b/app/interactions/domains/force_delete/check_discarded.rb @@ -0,0 +1,12 @@ +module Domains + module ForceDelete + class CheckDiscarded < Base + def execute + return true unless domain.discarded? + + message = 'Force delete procedure cannot be scheduled while a domain is discarded' + errors.add(:domain, message) + end + end + end +end diff --git a/app/interactions/domains/force_delete/notify_by_email.rb b/app/interactions/domains/force_delete/notify_by_email.rb new file mode 100644 index 000000000..b60f54a5e --- /dev/null +++ b/app/interactions/domains/force_delete/notify_by_email.rb @@ -0,0 +1,23 @@ +module Domains + module ForceDelete + class NotifyByEmail < Base + def execute + return unless notify_by_email + + if type == :fast_track + send_email + domain.update(contact_notification_sent_date: Time.zone.today) + else + domain.update(template_name: domain.notification_template) + end + end + + def send_email + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.notification_template).deliver_now + end + end + end +end diff --git a/app/interactions/domains/force_delete/notify_registrar.rb b/app/interactions/domains/force_delete/notify_registrar.rb new file mode 100644 index 000000000..522502640 --- /dev/null +++ b/app/interactions/domains/force_delete/notify_registrar.rb @@ -0,0 +1,12 @@ +module Domains + module ForceDelete + class NotifyRegistrar < Base + def execute + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + end + end +end diff --git a/app/interactions/domains/force_delete/post_set_process.rb b/app/interactions/domains/force_delete/post_set_process.rb new file mode 100644 index 000000000..904149f93 --- /dev/null +++ b/app/interactions/domains/force_delete/post_set_process.rb @@ -0,0 +1,19 @@ +module Domains + module ForceDelete + class PostSetProcess < Base + def execute + statuses = domain.statuses + # Stop all pending actions + statuses.delete(DomainStatus::PENDING_UPDATE) + statuses.delete(DomainStatus::PENDING_TRANSFER) + statuses.delete(DomainStatus::PENDING_RENEW) + statuses.delete(DomainStatus::PENDING_CREATE) + + # Allow deletion + statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) + statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/force_delete/prepare_domain.rb b/app/interactions/domains/force_delete/prepare_domain.rb new file mode 100644 index 000000000..74eea21ed --- /dev/null +++ b/app/interactions/domains/force_delete/prepare_domain.rb @@ -0,0 +1,15 @@ +module Domains + module ForceDelete + class PrepareDomain < Base + STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, + DomainStatus::SERVER_RENEW_PROHIBITED, + DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze + + def execute + domain.statuses_before_force_delete = domain.statuses + domain.statuses |= STATUSES_TO_SET + domain.save(validate: false) + end + end + end +end diff --git a/app/interactions/domains/force_delete/set_force_delete.rb b/app/interactions/domains/force_delete/set_force_delete.rb new file mode 100644 index 000000000..16a0b09fa --- /dev/null +++ b/app/interactions/domains/force_delete/set_force_delete.rb @@ -0,0 +1,14 @@ +module Domains + module ForceDelete + class SetForceDelete < Base + def execute + compose(CheckDiscarded, inputs) + compose(PrepareDomain, inputs) + compose(SetStatus, inputs) + compose(PostSetProcess, inputs) + compose(NotifyRegistrar, inputs) + compose(NotifyByEmail, inputs) + end + end + end +end diff --git a/app/interactions/domains/force_delete/set_status.rb b/app/interactions/domains/force_delete/set_status.rb new file mode 100644 index 000000000..b0a53ad82 --- /dev/null +++ b/app/interactions/domains/force_delete/set_status.rb @@ -0,0 +1,40 @@ +module Domains + module ForceDelete + class SetStatus < Base + def execute + domain.force_delete_type = type + type == :fast_track ? force_delete_fast_track : force_delete_soft + domain.save(validate: false) + end + + def force_delete_fast_track + domain.force_delete_date = Time.zone.today + + expire_warning_period_days + + redemption_grace_period_days + domain.force_delete_start = Time.zone.today + 1.day + end + + def force_delete_soft + years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 + soft_forcedelete_dates(years) if years.positive? + end + + private + + def soft_forcedelete_dates(years) + domain.force_delete_start = domain.valid_to - years.years + domain.force_delete_date = domain.force_delete_start + + Setting.expire_warning_period.days + + Setting.redemption_grace_period.days + end + + def redemption_grace_period_days + Setting.redemption_grace_period.days + 1.day + end + + def expire_warning_period_days + Setting.expire_warning_period.days + end + end + end +end diff --git a/app/interactions/force_delete_interaction/base.rb b/app/interactions/force_delete_interaction/base.rb deleted file mode 100644 index 4764ce8fe..000000000 --- a/app/interactions/force_delete_interaction/base.rb +++ /dev/null @@ -1,16 +0,0 @@ -module ForceDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to set ForceDelete on' - symbol :type, - default: :fast_track, - description: 'Force delete type, might be :fast_track or :soft' - boolean :notify_by_email, - default: false, - description: 'Do we need to send email notification' - - validates :type, inclusion: { in: %i[fast_track soft] } - end -end - diff --git a/app/interactions/force_delete_interaction/check_discarded.rb b/app/interactions/force_delete_interaction/check_discarded.rb deleted file mode 100644 index e0c893294..000000000 --- a/app/interactions/force_delete_interaction/check_discarded.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ForceDeleteInteraction - class CheckDiscarded < Base - def execute - return true unless domain.discarded? - - message = 'Force delete procedure cannot be scheduled while a domain is discarded' - errors.add(:domain, message) - end - end -end - diff --git a/app/interactions/force_delete_interaction/notify_by_email.rb b/app/interactions/force_delete_interaction/notify_by_email.rb deleted file mode 100644 index 541df258d..000000000 --- a/app/interactions/force_delete_interaction/notify_by_email.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ForceDeleteInteraction - class NotifyByEmail < Base - def execute - return unless notify_by_email - - if type == :fast_track - send_email - domain.update(contact_notification_sent_date: Time.zone.today) - else - domain.update(template_name: domain.notification_template) - end - end - - def send_email - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.notification_template).deliver_now - end - end -end diff --git a/app/interactions/force_delete_interaction/notify_registrar.rb b/app/interactions/force_delete_interaction/notify_registrar.rb deleted file mode 100644 index 691e54f7e..000000000 --- a/app/interactions/force_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,10 +0,0 @@ -module ForceDeleteInteraction - class NotifyRegistrar < Base - def execute - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - end -end diff --git a/app/interactions/force_delete_interaction/post_set_process.rb b/app/interactions/force_delete_interaction/post_set_process.rb deleted file mode 100644 index e51acf9b7..000000000 --- a/app/interactions/force_delete_interaction/post_set_process.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ForceDeleteInteraction - class PostSetProcess < Base - def execute - statuses = domain.statuses - # Stop all pending actions - statuses.delete(DomainStatus::PENDING_UPDATE) - statuses.delete(DomainStatus::PENDING_TRANSFER) - statuses.delete(DomainStatus::PENDING_RENEW) - statuses.delete(DomainStatus::PENDING_CREATE) - - # Allow deletion - statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) - statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED) - domain.save(validate: false) - end - end -end diff --git a/app/interactions/force_delete_interaction/prepare_domain.rb b/app/interactions/force_delete_interaction/prepare_domain.rb deleted file mode 100644 index 97f364145..000000000 --- a/app/interactions/force_delete_interaction/prepare_domain.rb +++ /dev/null @@ -1,13 +0,0 @@ -module ForceDeleteInteraction - class PrepareDomain < Base - STATUSES_TO_SET = [DomainStatus::FORCE_DELETE, - DomainStatus::SERVER_RENEW_PROHIBITED, - DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze - - def execute - domain.statuses_before_force_delete = domain.statuses - domain.statuses |= STATUSES_TO_SET - domain.save(validate: false) - end - end -end diff --git a/app/interactions/force_delete_interaction/set_force_delete.rb b/app/interactions/force_delete_interaction/set_force_delete.rb deleted file mode 100644 index 4608b5127..000000000 --- a/app/interactions/force_delete_interaction/set_force_delete.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ForceDeleteInteraction - class SetForceDelete < Base - def execute - compose(CheckDiscarded, inputs) - compose(PrepareDomain, inputs) - compose(SetStatus, inputs) - compose(PostSetProcess, inputs) - compose(NotifyRegistrar, inputs) - compose(NotifyByEmail, inputs) - end - end -end diff --git a/app/interactions/force_delete_interaction/set_status.rb b/app/interactions/force_delete_interaction/set_status.rb deleted file mode 100644 index 7d104aeee..000000000 --- a/app/interactions/force_delete_interaction/set_status.rb +++ /dev/null @@ -1,38 +0,0 @@ -module ForceDeleteInteraction - class SetStatus < Base - def execute - domain.force_delete_type = type - type == :fast_track ? force_delete_fast_track : force_delete_soft - domain.save(validate: false) - end - - def force_delete_fast_track - domain.force_delete_date = Time.zone.today + - expire_warning_period_days + - redemption_grace_period_days - domain.force_delete_start = Time.zone.today + 1.day - end - - def force_delete_soft - years = (domain.valid_to.to_date - Time.zone.today).to_i / 365 - soft_forcedelete_dates(years) if years.positive? - end - - private - - def soft_forcedelete_dates(years) - domain.force_delete_start = domain.valid_to - years.years - domain.force_delete_date = domain.force_delete_start + - Setting.expire_warning_period.days + - Setting.redemption_grace_period.days - end - - def redemption_grace_period_days - Setting.redemption_grace_period.days + 1.day - end - - def expire_warning_period_days - Setting.expire_warning_period.days - end - end -end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index ff869fc0a..6e0f6c886 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -53,13 +53,13 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end def schedule_force_delete(type: :fast_track, notify_by_email: false) - ForceDeleteInteraction::SetForceDelete.run(domain: self, - type: type, - notify_by_email: notify_by_email) + Domains::ForceDelete::SetForceDelete.run(domain: self, + type: type, + notify_by_email: notify_by_email) end def cancel_force_delete - CancelForceDeleteInteraction::CancelForceDelete.run(domain: self) + Domains::CancelForceDelete::CancelForceDelete.run(domain: self) end def outzone_date diff --git a/app/models/domain.rb b/app/models/domain.rb index 1ef66ffab..dc7d86da8 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -418,7 +418,7 @@ class Domain < ApplicationRecord pending_delete_confirmation! save(validate: false) # should check if this did succeed - DomainDeleteConfirmInteraction::SendRequest.run(domain: self) + Domains::DeleteConfirm::SendRequest.run(domain: self) end def cancel_pending_delete diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index 7c205fa74..dcb9b80f6 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -113,7 +113,7 @@ class ForceDeleteTest < ActionMailer::TestCase def test_force_delete_cannot_be_scheduled_when_a_domain_is_discarded @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) - result = ForceDeleteInteraction::SetForceDelete.run(domain: @domain, type: :fast_track) + result = Domains::ForceDelete::SetForceDelete.run(domain: @domain, type: :fast_track) assert_not result.valid? assert_not @domain.force_delete_scheduled? diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index cc88cf35f..ae12f4a1e 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -414,7 +414,7 @@ class DomainTest < ActiveSupport::TestCase force_delete_date: nil) @domain.update(template_name: 'legal_person') travel_to Time.zone.parse('2010-07-05') - ForceDeleteInteraction::SetForceDelete.run!(domain: @domain, type: :fast_track) + Domains::ForceDelete::SetForceDelete.run!(domain: @domain, type: :fast_track) assert(@domain.force_delete_scheduled?) other_registrant = Registrant.find_by(code: 'jane-001') @domain.pending_json['new_registrant_id'] = other_registrant.id From 8ff8aa78c8e12ae6523c3cc0ff1524b12de44d81 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 16:08:32 +0500 Subject: [PATCH 134/202] Move interactor to namespace --- .../client_hold_interaction/base.rb | 8 --- .../process_client_hold.rb | 67 ------------------ .../set_client_hold.rb | 15 ---- app/interactions/domains/client_hold/base.rb | 10 +++ .../client_hold/process_client_hold.rb | 69 +++++++++++++++++++ .../domains/client_hold/set_client_hold.rb | 17 +++++ app/models/domain_cron.rb | 2 +- test/models/domain/force_delete_test.rb | 10 +-- 8 files changed, 102 insertions(+), 96 deletions(-) delete mode 100644 app/interactions/client_hold_interaction/base.rb delete mode 100644 app/interactions/client_hold_interaction/process_client_hold.rb delete mode 100644 app/interactions/client_hold_interaction/set_client_hold.rb create mode 100644 app/interactions/domains/client_hold/base.rb create mode 100644 app/interactions/domains/client_hold/process_client_hold.rb create mode 100644 app/interactions/domains/client_hold/set_client_hold.rb diff --git a/app/interactions/client_hold_interaction/base.rb b/app/interactions/client_hold_interaction/base.rb deleted file mode 100644 index be8b8b0a8..000000000 --- a/app/interactions/client_hold_interaction/base.rb +++ /dev/null @@ -1,8 +0,0 @@ -module ClientHoldInteraction - class Base < ActiveInteraction::Base - def to_stdout(message) - time = Time.zone.now.utc - STDOUT << "#{time} - #{message}\n" unless Rails.env.test? - end - end -end diff --git a/app/interactions/client_hold_interaction/process_client_hold.rb b/app/interactions/client_hold_interaction/process_client_hold.rb deleted file mode 100644 index 453c400cc..000000000 --- a/app/interactions/client_hold_interaction/process_client_hold.rb +++ /dev/null @@ -1,67 +0,0 @@ -module ClientHoldInteraction - class ProcessClientHold < Base - object :domain, - class: Domain, - description: 'Domain to set ClientHold on' - - # rubocop:disable Metrics/AbcSize - def execute - notify_on_grace_period if should_notify_on_soft_force_delete? - - return unless client_holdable? - - domain.statuses << DomainStatus::CLIENT_HOLD - to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") - - domain.save(validate: false) - notify_client_hold - - to_stdout("Successfully set client_hold on (#{domain.name})") - end - - def notify_on_grace_period - domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', - domain_name: domain.name, - date: domain.force_delete_start)) - send_mail if domain.template_name.present? - domain.update(contact_notification_sent_date: Time.zone.today) - end - - def notify_client_hold - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) - end - - def send_mail - DomainDeleteMailer.forced(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant, - template_name: domain.template_name).deliver_now - end - - def should_notify_on_soft_force_delete? - domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && - domain.force_delete_start.to_date <= Time.zone.now.to_date && - domain.force_delete_type.to_sym == :soft && - !domain.statuses.include?(DomainStatus::CLIENT_HOLD) - end - # rubocop:enable Metrics/AbcSize - - def client_holdable? - domain.force_delete_scheduled? && - !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && - domain.force_delete_start.present? && - force_delete_lte_today && force_delete_lte_valid_date - end - - def force_delete_lte_today - domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now - end - - def force_delete_lte_valid_date - domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to - end - end -end diff --git a/app/interactions/client_hold_interaction/set_client_hold.rb b/app/interactions/client_hold_interaction/set_client_hold.rb deleted file mode 100644 index d723d5cc8..000000000 --- a/app/interactions/client_hold_interaction/set_client_hold.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ClientHoldInteraction - class SetClientHold < Base - def execute - to_stdout('Setting client_hold to domains\n') - - ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" - - ::Domain.force_delete_scheduled.each do |domain| - ClientHoldInteraction::ProcessClientHold.run(domain: domain) - end - - to_stdout('All client_hold setting are done\n') - end - end -end diff --git a/app/interactions/domains/client_hold/base.rb b/app/interactions/domains/client_hold/base.rb new file mode 100644 index 000000000..c3c626b79 --- /dev/null +++ b/app/interactions/domains/client_hold/base.rb @@ -0,0 +1,10 @@ +module Domains + module ClientHold + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/client_hold/process_client_hold.rb b/app/interactions/domains/client_hold/process_client_hold.rb new file mode 100644 index 000000000..8b8d60403 --- /dev/null +++ b/app/interactions/domains/client_hold/process_client_hold.rb @@ -0,0 +1,69 @@ +module Domains + module ClientHold + class ProcessClientHold < Base + object :domain, + class: Domain, + description: 'Domain to set ClientHold on' + + # rubocop:disable Metrics/AbcSize + def execute + notify_on_grace_period if should_notify_on_soft_force_delete? + + return unless client_holdable? + + domain.statuses << DomainStatus::CLIENT_HOLD + to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n") + + domain.save(validate: false) + notify_client_hold + + to_stdout("Successfully set client_hold on (#{domain.name})") + end + + def notify_on_grace_period + domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain', + domain_name: domain.name, + date: domain.force_delete_start)) + send_mail if domain.template_name.present? + domain.update(contact_notification_sent_date: Time.zone.today) + end + + def notify_client_hold + domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date)) + end + + def send_mail + DomainDeleteMailer.forced(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant, + template_name: domain.template_name).deliver_now + end + + def should_notify_on_soft_force_delete? + domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? && + domain.force_delete_start.to_date <= Time.zone.now.to_date && + domain.force_delete_type.to_sym == :soft && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) + end + # rubocop:enable Metrics/AbcSize + + def client_holdable? + domain.force_delete_scheduled? && + !domain.statuses.include?(DomainStatus::CLIENT_HOLD) && + domain.force_delete_start.present? && + force_delete_lte_today && force_delete_lte_valid_date + end + + def force_delete_lte_today + domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now + end + + def force_delete_lte_valid_date + domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to + end + end + end +end diff --git a/app/interactions/domains/client_hold/set_client_hold.rb b/app/interactions/domains/client_hold/set_client_hold.rb new file mode 100644 index 000000000..0e54b531f --- /dev/null +++ b/app/interactions/domains/client_hold/set_client_hold.rb @@ -0,0 +1,17 @@ +module Domains + module ClientHold + class SetClientHold < Base + def execute + to_stdout('Setting client_hold to domains\n') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + ::Domain.force_delete_scheduled.each do |domain| + Domains::ClientHold::ProcessClientHold.run(domain: domain) + end + + to_stdout('All client_hold setting are done\n') + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index 613762062..d09141d52 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -79,6 +79,6 @@ class DomainCron end def self.start_client_hold - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! end end diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index d9e7a3632..304a7a18b 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -206,7 +206,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_emails 1 @@ -227,7 +227,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-08-21') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_emails 1 @@ -244,7 +244,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :soft) travel_to Time.zone.parse('2010-07-06') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) @@ -259,7 +259,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-25') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_includes(@domain.statuses, asserted_status) @@ -275,7 +275,7 @@ class ForceDeleteTest < ActionMailer::TestCase @domain.schedule_force_delete(type: :fast_track) travel_to Time.zone.parse('2010-07-06') - ClientHoldInteraction::SetClientHold.run! + Domains::ClientHold::SetClientHold.run! @domain.reload assert_not_includes(@domain.statuses, asserted_status) From cb7ff317e42a8a1dd2105834301e065e0ab676f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Mon, 30 Nov 2020 14:26:46 +0200 Subject: [PATCH 135/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44329c4d0..f418d4034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +30.11.2020 +* Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762) + 27.11.2020 * Refactored delete confirmation for interactors [#1753](https://github.com/internetee/registry/issues/1753) From dc5bbb587f1490eb714742ff7db3acefc30a8c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 16:52:28 +0200 Subject: [PATCH 136/202] EPP: Show error when trying to remove unassigned domain status --- app/models/epp/domain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index c46732712..a5922f48c 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -417,7 +417,7 @@ class Epp::Domain < Domain if statuses.include?(x) to_destroy << x else - add_epp_error('2303', 'status', x, [:domain_statuses, :not_found]) + add_epp_error('2303', 'status', x, [:statuses, :not_found]) end end @@ -432,7 +432,7 @@ class Epp::Domain < Domain frame.css('status').each do |x| unless DomainStatus::CLIENT_STATUSES.include?(x['s']) - add_epp_error('2303', 'status', x['s'], [:domain_statuses, :not_found]) + add_epp_error('2303', 'status', x['s'], [:statuses, :not_found]) next end From 490de2ee4bd89b83df277e8848efbdbe479edbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 17:05:21 +0200 Subject: [PATCH 137/202] Add test for unassigned domain status removal --- .../epp/domain/update/base_test.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index 6ce455948..14e806fca 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -503,6 +503,30 @@ class EppDomainUpdateBaseTest < EppTestCase assert_not_includes(@domain.statuses, DomainStatus::CLIENT_HOLD) end + def test_update_domain_returns_error_when_removing_unassigned_status + assert_not_includes(@domain.statuses, DomainStatus::CLIENT_HOLD) + request_xml = <<-XML + + + + + + #{@domain.name} + + + + + + + + XML + + post epp_update_path, params: { frame: request_xml }, + headers: { 'HTTP_COOKIE' => 'session=api_bestnames' } + @domain.reload + assert_epp_response :object_does_not_exist + end + private def assert_verification_and_notification_emails From e5a2a6cf0f33281c8f16874f5e3343bcf635465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 17:10:04 +0200 Subject: [PATCH 138/202] Fix CC issues --- app/models/epp/domain.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index a5922f48c..7fb23a6e9 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -417,7 +417,7 @@ class Epp::Domain < Domain if statuses.include?(x) to_destroy << x else - add_epp_error('2303', 'status', x, [:statuses, :not_found]) + add_epp_error('2303', 'status', x, %i[statuses not_found]) end end @@ -432,7 +432,7 @@ class Epp::Domain < Domain frame.css('status').each do |x| unless DomainStatus::CLIENT_STATUSES.include?(x['s']) - add_epp_error('2303', 'status', x['s'], [:statuses, :not_found]) + add_epp_error('2303', 'status', x['s'], %i[statuses not_found]) next end From 512b181cd785e79d087f3d491d13ad6ad4787db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 1 Dec 2020 10:00:04 +0200 Subject: [PATCH 139/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f418d4034..489ba94d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +01.12.2020 +* Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) + 30.11.2020 * Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762) From c9d4aaded98c98e0b9dc38ed5d64861493ea0ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 30 Nov 2020 16:50:14 +0200 Subject: [PATCH 140/202] Fix registrant domain serializer --- .../api/v1/registrant/domains_controller.rb | 3 ++- lib/serializers/registrant_api/domain.rb | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/registrant/domains_controller.rb b/app/controllers/api/v1/registrant/domains_controller.rb index 390148488..73b534598 100644 --- a/app/controllers/api/v1/registrant/domains_controller.rb +++ b/app/controllers/api/v1/registrant/domains_controller.rb @@ -7,6 +7,7 @@ module Api def index limit = params[:limit] || 200 offset = params[:offset] || 0 + simple = params[:simple] == 'true' || false if limit.to_i > 200 || limit.to_i < 1 render(json: { errors: [{ limit: ['parameter is out of range'] }] }, @@ -20,7 +21,7 @@ module Api domains = current_user_domains serialized_domains = domains.limit(limit).offset(offset).map do |item| - serializer = Serializers::RegistrantApi::Domain.new(item, simplify: true) + serializer = Serializers::RegistrantApi::Domain.new(item, simplify: simple) serializer.to_json end diff --git a/lib/serializers/registrant_api/domain.rb b/lib/serializers/registrant_api/domain.rb index f0c9c1876..29362ad5c 100644 --- a/lib/serializers/registrant_api/domain.rb +++ b/lib/serializers/registrant_api/domain.rb @@ -9,18 +9,23 @@ module Serializers end def to_json - if simplify + if @simplify return { id: domain.uuid, name: domain.name, registered_at: domain.registered_at, valid_to: domain.valid_to, + outzone_at: domain.outzone_at, registrant_verification_asked_at: domain.registrant_verification_asked_at, statuses: domain.statuses, registrar: { name: domain.registrar.name, - website: domain.registrar.website - } + website: domain.registrar.website, + }, + registrant: { + name: domain.registrant.name, + id: domain.registrant.uuid, + }, } end From 15e090c3b1710f13ab63ecf25c2bfa5023e54043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Tue, 1 Dec 2020 12:53:30 +0200 Subject: [PATCH 141/202] Fix CC issue --- lib/serializers/registrant_api/domain.rb | 32 +++++++++--------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/lib/serializers/registrant_api/domain.rb b/lib/serializers/registrant_api/domain.rb index 29362ad5c..f8d5bb830 100644 --- a/lib/serializers/registrant_api/domain.rb +++ b/lib/serializers/registrant_api/domain.rb @@ -8,26 +8,8 @@ module Serializers @simplify = simplify end - def to_json - if @simplify - return { - id: domain.uuid, - name: domain.name, - registered_at: domain.registered_at, - valid_to: domain.valid_to, - outzone_at: domain.outzone_at, - registrant_verification_asked_at: domain.registrant_verification_asked_at, - statuses: domain.statuses, - registrar: { - name: domain.registrar.name, - website: domain.registrar.website, - }, - registrant: { - name: domain.registrant.name, - id: domain.registrant.uuid, - }, - } - end + def to_json(_obj = nil) + return simple_object if @simplify { id: domain.uuid, @@ -70,6 +52,16 @@ module Serializers private + def simple_object + { + id: domain.uuid, name: domain.name, registered_at: domain.registered_at, + valid_to: domain.valid_to, outzone_at: domain.outzone_at, statuses: domain.statuses, + registrant_verification_asked_at: domain.registrant_verification_asked_at, + registrar: { name: domain.registrar.name, website: domain.registrar.website }, + registrant: { name: domain.registrant.name, id: domain.registrant.uuid } + } + end + def dnssec_keys domain.dnskeys.map do |key| "#{key.flags} #{key.protocol} #{key.alg} #{key.public_key}" From 8916cb29d1f631f690371abe24f6d80d50efd071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 1 Dec 2020 15:48:13 +0200 Subject: [PATCH 142/202] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 489ba94d3..9774d5cb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 01.12.2020 * Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) +* Fixed internal error on removing clientHold status when not present [#1766](https://github.com/internetee/registry/issues/1766) 30.11.2020 * Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762) From 15d2ffe200837ddfde536bfb22474ce3fb1099ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Thu, 3 Dec 2020 16:12:36 +0200 Subject: [PATCH 143/202] Registrant API: Return associated domains for contact query --- .../api/v1/registrant/contacts_controller.rb | 11 ++++++----- app/models/contact.rb | 13 +++++++++---- lib/serializers/registrant_api/contact.rb | 13 +++++++++---- lib/serializers/registrant_api/domain.rb | 3 ++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index 10f9abacf..e11458ff8 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -19,15 +19,16 @@ module Api end contacts = current_user_contacts.limit(limit).offset(offset) - serialized_contacts = contacts.collect { |contact| serialize_contact(contact) } + serialized_contacts = contacts.collect { |contact| serialize_contact(contact, false) } render json: serialized_contacts end def show contact = current_user_contacts.find_by(uuid: params[:uuid]) + links = params[:links] == 'true' if contact - render json: serialize_contact(contact) + render json: serialize_contact(contact, links) else render json: { errors: [{ base: ['Contact not found'] }] }, status: :not_found end @@ -85,7 +86,7 @@ module Api contact.registrar.notify(action) end - render json: serialize_contact(contact) + render json: serialize_contact(contact, false) end private @@ -96,8 +97,8 @@ module Api current_registrant_user.direct_contacts end - def serialize_contact(contact) - Serializers::RegistrantApi::Contact.new(contact).to_json + def serialize_contact(contact, links) + Serializers::RegistrantApi::Contact.new(contact, links).to_json end end end diff --git a/app/models/contact.rb b/app/models/contact.rb index 8a154c50c..e30312b4a 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -347,19 +347,24 @@ class Contact < ApplicationRecord @desc = {} registrant_domains.each do |dom| - @desc[dom.name] ||= [] - @desc[dom.name] << :registrant + @desc[dom.name] ||= { id: dom.uuid, roles: [] } + @desc[dom.name][:roles] << :registrant end domain_contacts.each do |dc| - @desc[dc.domain.name] ||= [] - @desc[dc.domain.name] << dc.name.downcase.to_sym + @desc[dc.domain.name] ||= { id: dc.domain.uuid, roles: [] } + @desc[dc.domain.name][:roles] << dc.name.downcase.to_sym @desc[dc.domain.name] = @desc[dc.domain.name].compact end @desc end + def related_domains + a = related_domain_descriptions + a.keys.map { |d| { name: d, id: a[d][:id], roles: a[d][:roles] } } + end + def status_notes_array=(notes) self.status_notes = {} notes ||= [] diff --git a/lib/serializers/registrant_api/contact.rb b/lib/serializers/registrant_api/contact.rb index dd36b4400..023544174 100644 --- a/lib/serializers/registrant_api/contact.rb +++ b/lib/serializers/registrant_api/contact.rb @@ -1,14 +1,15 @@ module Serializers module RegistrantApi class Contact - attr_reader :contact + attr_reader :contact, :links - def initialize(contact) + def initialize(contact, links) @contact = contact + @links = links end - def to_json - { + def to_json(_obj = nil) + obj = { id: contact.uuid, name: contact.name, code: contact.code, @@ -31,6 +32,10 @@ module Serializers statuses: contact.statuses, disclosed_attributes: contact.disclosed_attributes, } + + obj[:links] = contact.related_domains if @links + + obj end end end diff --git a/lib/serializers/registrant_api/domain.rb b/lib/serializers/registrant_api/domain.rb index f8d5bb830..2359d77c9 100644 --- a/lib/serializers/registrant_api/domain.rb +++ b/lib/serializers/registrant_api/domain.rb @@ -58,7 +58,8 @@ module Serializers valid_to: domain.valid_to, outzone_at: domain.outzone_at, statuses: domain.statuses, registrant_verification_asked_at: domain.registrant_verification_asked_at, registrar: { name: domain.registrar.name, website: domain.registrar.website }, - registrant: { name: domain.registrant.name, id: domain.registrant.uuid } + registrant: { name: domain.registrant.name, id: domain.registrant.uuid, + phone: domain.registrant.phone, email: domain.registrant.email } } end From 92b294e81b10e69109e20f2e3d1d54e0d81b6c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 4 Dec 2020 15:24:14 +0200 Subject: [PATCH 144/202] Reflect new behaviour of registrant API in tests --- .../api/registrant/registrant_api_domains_test.rb | 8 ++++---- test/lib/serializers/registrant_api/contact_test.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/api/registrant/registrant_api_domains_test.rb b/test/integration/api/registrant/registrant_api_domains_test.rb index 22516fecc..61d635e5f 100644 --- a/test/integration/api/registrant/registrant_api_domains_test.rb +++ b/test/integration/api/registrant/registrant_api_domains_test.rb @@ -50,10 +50,10 @@ class RegistrantApiDomainsTest < ApplicationIntegrationTest assert_equal(200, response.status) response_json = JSON.parse(response.body, symbolize_names: true) - array_of_domain_names = response_json.map { |x| x[:name] } + array_of_domain_names = response_json[:domains].map { |x| x[:name] } assert(array_of_domain_names.include?('hospital.test')) - array_of_domain_registrars = response_json.map { |x| x[:registrar] } + array_of_domain_registrars = response_json[:domains].map { |x| x[:registrar] } assert(array_of_domain_registrars.include?({name: 'Good Names', website: nil})) end @@ -63,12 +63,12 @@ class RegistrantApiDomainsTest < ApplicationIntegrationTest response_json = JSON.parse(response.body, symbolize_names: true) assert_equal(200, response.status) - assert_equal(2, response_json.count) + assert_equal(2, response_json[:domains].count) get '/api/v1/registrant/domains', headers: @auth_headers response_json = JSON.parse(response.body, symbolize_names: true) - assert_equal(4, response_json.count) + assert_equal(4, response_json[:domains].count) end def test_root_does_not_accept_limit_higher_than_200 diff --git a/test/lib/serializers/registrant_api/contact_test.rb b/test/lib/serializers/registrant_api/contact_test.rb index 165c91e00..8b84abd38 100644 --- a/test/lib/serializers/registrant_api/contact_test.rb +++ b/test/lib/serializers/registrant_api/contact_test.rb @@ -4,7 +4,7 @@ require 'serializers/registrant_api/contact' class SerializersRegistrantApiContactTest < ActiveSupport::TestCase def setup @contact = contacts(:william) - @serializer = Serializers::RegistrantApi::Contact.new(@contact) + @serializer = Serializers::RegistrantApi::Contact.new(@contact, false) @json = @serializer.to_json end From 36ff1fcf39b7f6ddf48b9c6157be39ac0760f1f2 Mon Sep 17 00:00:00 2001 From: Georg Date: Tue, 17 Nov 2020 14:27:12 +0200 Subject: [PATCH 145/202] Create ruby.yml example workflow Update ruby.yml limit builds Update ruby.yml postgres service Update ruby.yml pg_port copy config files Update ruby.yml Update ruby.yml Update database_travis.yml Update ruby.yml Update ruby.yml bump wkhtmltopdf-binary Unlock gem 'wkhtmltopdf-binary' Update ruby.yml Update ruby.yml Update ruby.yml remove Lockfile restore Gemfile test only ubuntu-18.04 bundle env remove deploy deps remove mina dep use rexml from bundle rather then std-lib to support ruby 3.0 install rexml drop ruby 3.0, cleanup workflow fix codeclimate sorting issue Codeclimate reporting stop using deprecated set-env use simplecov formater simplecov in after-build fix yaml syntax run in debug mode Set correct env var to run coverage cleanup, provide CC_TEST_REPORTER_ID secret combine multiple results more variables pull/commit different envs Change test command to get more coverage --- .github/workflows/ruby.yml | 117 +++++++++++++++++++++++++++++++++++++ Gemfile | 6 +- Gemfile.lock | 14 +---- config/database_travis.yml | 2 +- test/test_helper.rb | 1 + 5 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/ruby.yml diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 000000000..eea0ccc03 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,117 @@ +name: Github Testing +on: [push] + + +jobs: + test: + services: + postgres: + image: postgres:12 + ports: ["5432:5432"] + env: + POSTGRES_PASSWORD: password + POSTGRES_USERNAME: postgres + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + ruby: [2.6, 2.7 ] + runs-on: ${{ matrix.os }} + continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} + steps: + + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: config bundler + run: | + bundle config set without 'development staging production' + bundle config set deployment '[secure]' + bundle env + head -n1 $(which bundle) + + - name: Set ENV for codeclimate (pull_request) + run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/$GITHUB_HEAD_REF:refs/remotes/origin/$GITHUB_HEAD_REF + echo "GIT_BRANCH=$GITHUB_HEAD_REF" >> $GITHUB_ENV + echo "GIT_COMMIT_SHA=$(git rev-parse origin/$GITHUB_HEAD_REF)" >> $GITHUB_ENV + if: github.event_name == 'pull_request' + + - name: Set ENV for codeclimate (push) + run: | + echo "GIT_BRANCH=$GITHUB_REF" >> $GITHUB_ENV + echo "GIT_COMMIT_SHA=$GITHUB_SHA" >> $GITHUB_ENV + if: github.event_name == 'push' + + - name: Prepare CodeClimate + env: + CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} + run: | + curl -LSs 'https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64' >./cc-test-reporter; + chmod +x ./cc-test-reporter + ./cc-test-reporter before-build + + - name: Run Tests + env: + PG_DATABASE: postgres + PG_HOST: localhost + PG_USER: postgres + PG_PASSWORD: password + PG_PORT: ${{ job.services.postgres.ports[5432] }} + RAILS_ENV: test + COVERAGE: true + DISABLE_SPRING: 1 + run: | + cp config/application.yml.sample config/application.yml + cp config/database_travis.yml config/database.yml + echo "openssl_config_path: 'test/fixtures/files/test_ca/openssl.cnf'" >> config/application.yml + echo "crl_dir: 'test/fixtures/files/test_ca/crl'" >> config/application.yml + echo "crl_path: 'test/fixtures/files/test_ca/crl/crl.pem'" >> config/application.yml + echo "ca_cert_path: 'test/fixtures/files/test_ca/certs/ca.crt.pem'" >> config/application.yml + echo "ca_key_path: 'test/fixtures/files/test_ca/private/ca.key.pem'" >> config/application.yml + echo "ca_key_password: 'password'" >> config/application.yml + bundle exec rake db:setup:all + bundle exec rails test test/* + - name: Save coverage + run: ./cc-test-reporter format-coverage --output coverage/codeclimate.${{ matrix.ruby }}.json + + - uses: actions/upload-artifact@v1 + with: + name: coverage-${{ matrix.ruby }} + path: coverage/codeclimate.${{ matrix.ruby }}.json + + upload_coverage: + runs-on: ubuntu-18.04 + + env: + CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} + CC_TEST_REPORTER_URL: https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 + + needs: test + + steps: + - name: Download test coverage reporter + run: curl -L $CC_TEST_REPORTER_URL > cc-test-reporter + + - name: Give test coverage reporter executable permissions + run: chmod +x cc-test-reporter + + - uses: actions/download-artifact@v1 + with: + name: coverage-2.6 + path: coverage + + - uses: actions/download-artifact@v1 + with: + name: coverage-2.7 + path: coverage + + - name: Aggregate & upload results to Code Climate + run: | + ./cc-test-reporter sum-coverage coverage/codeclimate.*.json + ./cc-test-reporter upload-coverage + diff --git a/Gemfile b/Gemfile index 7ae8d4ef6..9bbcba254 100644 --- a/Gemfile +++ b/Gemfile @@ -73,15 +73,11 @@ gem 'e_invoice', github: 'internetee/e_invoice', branch: :master gem 'lhv', github: 'internetee/lhv', branch: 'master' gem 'domain_name' gem 'haml', '~> 5.0' +gem 'rexml' gem 'wkhtmltopdf-binary', '~> 0.12.5.1' gem 'directo', github: 'internetee/directo', branch: 'master' -group :development do - # deploy - gem 'listen', '3.2.1' - gem 'mina', '0.3.1' # for fast deployment -end group :development, :test do gem 'pry', '0.10.1' diff --git a/Gemfile.lock b/Gemfile.lock index 72c716aae..2a0bb55b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,9 +250,6 @@ GEM kaminari-core (= 1.2.1) kaminari-core (1.2.1) libxml-ruby (3.2.0) - listen (3.2.1) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) logger (1.4.2) loofah (2.7.0) crass (~> 1.0.2) @@ -266,9 +263,6 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2020.0512) mimemagic (0.3.5) - mina (0.3.1) - open4 (~> 1.3.4) - rake mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.2) @@ -296,7 +290,6 @@ GEM omniauth-rails_csrf_protection (0.1.2) actionpack (>= 4.2) omniauth (>= 1.3.1) - open4 (1.3.4) openid_connect (1.2.0) activemodel attr_required (>= 1.0.0) @@ -370,9 +363,6 @@ GEM activesupport (>= 5.2.1) i18n polyamorous (= 2.3.2) - rb-fsevent (0.10.4) - rb-inotify (0.10.1) - ffi (~> 1.0) rbtree3 (0.6.0) regexp_parser (1.8.0) request_store (1.5.0) @@ -385,6 +375,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) + rexml (3.2.4) ruby2_keywords (0.0.2) rubyzip (2.3.0) sass-rails (6.0.0) @@ -519,8 +510,6 @@ DEPENDENCIES jquery-ui-rails (= 5.0.5) kaminari lhv! - listen (= 3.2.1) - mina (= 0.3.1) minitest (~> 5.14) money-rails nokogiri @@ -537,6 +526,7 @@ DEPENDENCIES rails (~> 6.0) ransack (~> 2.3) rest-client + rexml sass-rails select2-rails (= 3.5.9.3) selectize-rails (= 0.12.1) diff --git a/config/database_travis.yml b/config/database_travis.yml index b79e2c453..48ffd5e27 100644 --- a/config/database_travis.yml +++ b/config/database_travis.yml @@ -4,7 +4,7 @@ default: &default encoding: unicode pool: 5 username: postgres - password: + password: password test: <<: *default diff --git a/test/test_helper.rb b/test/test_helper.rb index 459d4f8f5..a1634f717 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,6 +6,7 @@ if ENV['COVERAGE'] add_filter '/lib/core_monkey_patches/' add_filter '/lib/daemons/' add_filter '/lib/gem_monkey_patches/' + add_filter '/lib/tasks/' end end From 1b8906a9ccb70d774357f67276384182baf58417 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 7 Dec 2020 13:55:50 +0500 Subject: [PATCH 146/202] Remove Travis support --- .travis.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 23d4ab6b1..000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: ruby -cache: bundler -env: - - DB=postgresql -before_install: - - "wget -N http://chromedriver.storage.googleapis.com/2.43/chromedriver_linux64.zip -P ~/" - - "unzip ~/chromedriver_linux64.zip -d ~/" - - "rm ~/chromedriver_linux64.zip" - - "sudo mv -f ~/chromedriver /usr/local/share/" - - "sudo chmod +x /usr/local/share/chromedriver" - - "sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver" - - "bundle config set without 'development staging production'" - - "bundle config set deployment '[secure]'" -before_script: - - "cp config/application.yml.sample config/application.yml" - - "echo \"openssl_config_path: 'test/fixtures/files/test_ca/openssl.cnf'\" >> config/application.yml" - - "echo \"crl_dir: 'test/fixtures/files/test_ca/crl'\" >> config/application.yml" - - "echo \"crl_path: 'test/fixtures/files/test_ca/crl/crl.pem'\" >> config/application.yml" - - "echo \"ca_cert_path: 'test/fixtures/files/test_ca/certs/ca.crt.pem'\" >> config/application.yml" - - "echo \"ca_key_path: 'test/fixtures/files/test_ca/private/ca.key.pem'\" >> config/application.yml" - - "echo \"ca_key_password: 'password'\" >> config/application.yml" - - "cp config/database_travis.yml config/database.yml" - - "bundle exec rake db:setup:all" - - "curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter" - - "chmod +x ./cc-test-reporter" - - "./cc-test-reporter before-build" -after_script: - - "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT" -script: - - "bundle exec rails test test/*" -services: - - postgresql -addons: - postgresql: "9.4" - chrome: stable From d4063349264a9ac3c5d19b4b1af3c6fdd1fe7465 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 25 Nov 2020 14:26:53 +0500 Subject: [PATCH 147/202] Move DomainDelete to interactor design pattern --- .../domain_delete_interaction/base.rb | 7 +++++++ .../domain_delete_interaction/delete.rb | 11 ++++++++++ .../nofity_registrar.rb | 12 +++++++++++ app/jobs/domain_delete_job.rb | 21 ++++++++++--------- 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 app/interactions/domain_delete_interaction/base.rb create mode 100644 app/interactions/domain_delete_interaction/delete.rb create mode 100644 app/interactions/domain_delete_interaction/nofity_registrar.rb diff --git a/app/interactions/domain_delete_interaction/base.rb b/app/interactions/domain_delete_interaction/base.rb new file mode 100644 index 000000000..b2895e1d1 --- /dev/null +++ b/app/interactions/domain_delete_interaction/base.rb @@ -0,0 +1,7 @@ +module DomainDeleteInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to delete' + end +end diff --git a/app/interactions/domain_delete_interaction/delete.rb b/app/interactions/domain_delete_interaction/delete.rb new file mode 100644 index 000000000..181431ca1 --- /dev/null +++ b/app/interactions/domain_delete_interaction/delete.rb @@ -0,0 +1,11 @@ +module DomainDeleteInteraction + class Delete < :Base + def execute + ::PaperTrail.request.whodunnit = "interaction - #{self.class.name}" + WhoisRecord.where(domain_id: domain.id).destroy_all + + domain.destroy + compose(NotifyRegistrar, inputs) + end + end +end diff --git a/app/interactions/domain_delete_interaction/nofity_registrar.rb b/app/interactions/domain_delete_interaction/nofity_registrar.rb new file mode 100644 index 000000000..81a66e6b0 --- /dev/null +++ b/app/interactions/domain_delete_interaction/nofity_registrar.rb @@ -0,0 +1,12 @@ +module DomainDeleteInteraction + class NotifyRegistrar < Delete + def execute + bye_bye = domain.versions.last + domain.registrar.notifications.create!( + text: "#{I18n.t(:domain_deleted)}: #{domain.name}", + attached_obj_id: bye_bye.id, + attached_obj_type: bye_bye.class.to_s + ) + end + end +end diff --git a/app/jobs/domain_delete_job.rb b/app/jobs/domain_delete_job.rb index 43e0bb844..a41a27f05 100644 --- a/app/jobs/domain_delete_job.rb +++ b/app/jobs/domain_delete_job.rb @@ -3,15 +3,16 @@ class DomainDeleteJob < Que::Job def run(domain_id) domain = Domain.find(domain_id) - ::PaperTrail.request.whodunnit = "job - #{self.class.name}" - WhoisRecord.where(domain_id: domain.id).destroy_all - - domain.destroy - bye_bye = domain.versions.last - domain.registrar.notifications.create!( - text: "#{I18n.t(:domain_deleted)}: #{domain.name}", - attached_obj_id: bye_bye.id, - attached_obj_type: bye_bye.class.to_s - ) + DomainDeleteInteraction::Delete.run(domain: domain) + # ::PaperTrail.request.whodunnit = "job - #{self.class.name}" + # WhoisRecord.where(domain_id: domain.id).destroy_all + # + # domain.destroy + # bye_bye = domain.versions.last + # domain.registrar.notifications.create!( + # text: "#{I18n.t(:domain_deleted)}: #{domain.name}", + # attached_obj_id: bye_bye.id, + # attached_obj_type: bye_bye.class.to_s + # ) end end From 4c7f9f202601bed993a9811d56993b8f7cc04070 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 25 Nov 2020 17:01:58 +0500 Subject: [PATCH 148/202] Add interactor tests --- .../domain_delete_interaction/delete.rb | 4 +-- ...ofity_registrar.rb => notify_registrar.rb} | 2 +- app/jobs/domain_delete_job.rb | 10 -------- .../domain_delete_interaction_delete_test.rb | 25 +++++++++++++++++++ 4 files changed, 28 insertions(+), 13 deletions(-) rename app/interactions/domain_delete_interaction/{nofity_registrar.rb => notify_registrar.rb} (90%) create mode 100644 test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb diff --git a/app/interactions/domain_delete_interaction/delete.rb b/app/interactions/domain_delete_interaction/delete.rb index 181431ca1..1a74aae50 100644 --- a/app/interactions/domain_delete_interaction/delete.rb +++ b/app/interactions/domain_delete_interaction/delete.rb @@ -1,11 +1,11 @@ module DomainDeleteInteraction - class Delete < :Base + class Delete < Base def execute ::PaperTrail.request.whodunnit = "interaction - #{self.class.name}" WhoisRecord.where(domain_id: domain.id).destroy_all domain.destroy - compose(NotifyRegistrar, inputs) + compose(DomainDeleteInteraction::NotifyRegistrar, inputs) end end end diff --git a/app/interactions/domain_delete_interaction/nofity_registrar.rb b/app/interactions/domain_delete_interaction/notify_registrar.rb similarity index 90% rename from app/interactions/domain_delete_interaction/nofity_registrar.rb rename to app/interactions/domain_delete_interaction/notify_registrar.rb index 81a66e6b0..990f74de6 100644 --- a/app/interactions/domain_delete_interaction/nofity_registrar.rb +++ b/app/interactions/domain_delete_interaction/notify_registrar.rb @@ -1,5 +1,5 @@ module DomainDeleteInteraction - class NotifyRegistrar < Delete + class NotifyRegistrar < Base def execute bye_bye = domain.versions.last domain.registrar.notifications.create!( diff --git a/app/jobs/domain_delete_job.rb b/app/jobs/domain_delete_job.rb index a41a27f05..49ef23aa3 100644 --- a/app/jobs/domain_delete_job.rb +++ b/app/jobs/domain_delete_job.rb @@ -4,15 +4,5 @@ class DomainDeleteJob < Que::Job domain = Domain.find(domain_id) DomainDeleteInteraction::Delete.run(domain: domain) - # ::PaperTrail.request.whodunnit = "job - #{self.class.name}" - # WhoisRecord.where(domain_id: domain.id).destroy_all - # - # domain.destroy - # bye_bye = domain.versions.last - # domain.registrar.notifications.create!( - # text: "#{I18n.t(:domain_deleted)}: #{domain.name}", - # attached_obj_id: bye_bye.id, - # attached_obj_type: bye_bye.class.to_s - # ) end end diff --git a/test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb b/test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb new file mode 100644 index 000000000..a44140b34 --- /dev/null +++ b/test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class DomainDeleteInteractionDeleteTest < ActiveSupport::TestCase + setup do + @domain = domains(:shop) + end + + def test_discards_domains_with_past_delete_date + @domain.update!(delete_date: '2010-07-04') + travel_to Time.zone.parse('2010-07-05') + + DomainDeleteInteraction::Delete.run(domain: @domain) + + assert @domain.destroyed? + end + + def test_sends_notification + @domain.update!(delete_date: '2010-07-04') + travel_to Time.zone.parse('2010-07-05') + + assert_difference '@domain.registrar.notifications.count', 1 do + DomainDeleteInteraction::Delete.run(domain: @domain) + end + end +end From 30ddf83ee42dd21cc4cfc77a6addadda4a6ea16f Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 16:20:24 +0500 Subject: [PATCH 149/202] Move interactor to namespace --- app/interactions/domain_delete_interaction/base.rb | 7 ------- .../domain_delete_interaction/delete.rb | 11 ----------- .../domain_delete_interaction/notify_registrar.rb | 12 ------------ app/interactions/domains/delete/base.rb | 9 +++++++++ app/interactions/domains/delete/do_delete.rb | 13 +++++++++++++ .../domains/delete/notify_registrar.rb | 14 ++++++++++++++ app/jobs/domain_delete_job.rb | 2 +- ...action_delete_test.rb => domain_delete_test.rb} | 6 +++--- 8 files changed, 40 insertions(+), 34 deletions(-) delete mode 100644 app/interactions/domain_delete_interaction/base.rb delete mode 100644 app/interactions/domain_delete_interaction/delete.rb delete mode 100644 app/interactions/domain_delete_interaction/notify_registrar.rb create mode 100644 app/interactions/domains/delete/base.rb create mode 100644 app/interactions/domains/delete/do_delete.rb create mode 100644 app/interactions/domains/delete/notify_registrar.rb rename test/interactions/domain_delete_interaction/{domain_delete_interaction_delete_test.rb => domain_delete_test.rb} (71%) diff --git a/app/interactions/domain_delete_interaction/base.rb b/app/interactions/domain_delete_interaction/base.rb deleted file mode 100644 index b2895e1d1..000000000 --- a/app/interactions/domain_delete_interaction/base.rb +++ /dev/null @@ -1,7 +0,0 @@ -module DomainDeleteInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to delete' - end -end diff --git a/app/interactions/domain_delete_interaction/delete.rb b/app/interactions/domain_delete_interaction/delete.rb deleted file mode 100644 index 1a74aae50..000000000 --- a/app/interactions/domain_delete_interaction/delete.rb +++ /dev/null @@ -1,11 +0,0 @@ -module DomainDeleteInteraction - class Delete < Base - def execute - ::PaperTrail.request.whodunnit = "interaction - #{self.class.name}" - WhoisRecord.where(domain_id: domain.id).destroy_all - - domain.destroy - compose(DomainDeleteInteraction::NotifyRegistrar, inputs) - end - end -end diff --git a/app/interactions/domain_delete_interaction/notify_registrar.rb b/app/interactions/domain_delete_interaction/notify_registrar.rb deleted file mode 100644 index 990f74de6..000000000 --- a/app/interactions/domain_delete_interaction/notify_registrar.rb +++ /dev/null @@ -1,12 +0,0 @@ -module DomainDeleteInteraction - class NotifyRegistrar < Base - def execute - bye_bye = domain.versions.last - domain.registrar.notifications.create!( - text: "#{I18n.t(:domain_deleted)}: #{domain.name}", - attached_obj_id: bye_bye.id, - attached_obj_type: bye_bye.class.to_s - ) - end - end -end diff --git a/app/interactions/domains/delete/base.rb b/app/interactions/domains/delete/base.rb new file mode 100644 index 000000000..434712f72 --- /dev/null +++ b/app/interactions/domains/delete/base.rb @@ -0,0 +1,9 @@ +module Domains + module Delete + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to delete' + end + end +end diff --git a/app/interactions/domains/delete/do_delete.rb b/app/interactions/domains/delete/do_delete.rb new file mode 100644 index 000000000..202c36938 --- /dev/null +++ b/app/interactions/domains/delete/do_delete.rb @@ -0,0 +1,13 @@ +module Domains + module Delete + class DoDelete < Base + def execute + ::PaperTrail.request.whodunnit = "interaction - #{self.class.name}" + WhoisRecord.where(domain_id: domain.id).destroy_all + + domain.destroy + compose(Domains::Delete::NotifyRegistrar, inputs) + end + end + end +end diff --git a/app/interactions/domains/delete/notify_registrar.rb b/app/interactions/domains/delete/notify_registrar.rb new file mode 100644 index 000000000..d6af00fd8 --- /dev/null +++ b/app/interactions/domains/delete/notify_registrar.rb @@ -0,0 +1,14 @@ +module Domains + module Delete + class NotifyRegistrar < Base + def execute + bye_bye = domain.versions.last + domain.registrar.notifications.create!( + text: "#{I18n.t(:domain_deleted)}: #{domain.name}", + attached_obj_id: bye_bye.id, + attached_obj_type: bye_bye.class.to_s + ) + end + end + end +end diff --git a/app/jobs/domain_delete_job.rb b/app/jobs/domain_delete_job.rb index 49ef23aa3..5577908c6 100644 --- a/app/jobs/domain_delete_job.rb +++ b/app/jobs/domain_delete_job.rb @@ -3,6 +3,6 @@ class DomainDeleteJob < Que::Job def run(domain_id) domain = Domain.find(domain_id) - DomainDeleteInteraction::Delete.run(domain: domain) + Domains::Delete::DoDelete.run(domain: domain) end end diff --git a/test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb b/test/interactions/domain_delete_interaction/domain_delete_test.rb similarity index 71% rename from test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb rename to test/interactions/domain_delete_interaction/domain_delete_test.rb index a44140b34..9582fae03 100644 --- a/test/interactions/domain_delete_interaction/domain_delete_interaction_delete_test.rb +++ b/test/interactions/domain_delete_interaction/domain_delete_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class DomainDeleteInteractionDeleteTest < ActiveSupport::TestCase +class DomainDeleteTest < ActiveSupport::TestCase setup do @domain = domains(:shop) end @@ -9,7 +9,7 @@ class DomainDeleteInteractionDeleteTest < ActiveSupport::TestCase @domain.update!(delete_date: '2010-07-04') travel_to Time.zone.parse('2010-07-05') - DomainDeleteInteraction::Delete.run(domain: @domain) + Domains::Delete::DoDelete.run(domain: @domain) assert @domain.destroyed? end @@ -19,7 +19,7 @@ class DomainDeleteInteractionDeleteTest < ActiveSupport::TestCase travel_to Time.zone.parse('2010-07-05') assert_difference '@domain.registrar.notifications.count', 1 do - DomainDeleteInteraction::Delete.run(domain: @domain) + Domains::Delete::DoDelete.run(domain: @domain) end end end From b4e26952d682342fcbcac42695762ab0a0b1a6c6 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 18:34:43 +0500 Subject: [PATCH 150/202] Fix domain deletion deadline --- app/models/concerns/domain/deletable.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/domain/deletable.rb b/app/models/concerns/domain/deletable.rb index bd01ab1c9..81518c739 100644 --- a/app/models/concerns/domain/deletable.rb +++ b/app/models/concerns/domain/deletable.rb @@ -20,6 +20,6 @@ module Concerns::Domain::Deletable end def deletion_deadline - delete_date + 24.hours + (delete_date || Time.zone.now) + 24.hours end -end \ No newline at end of file +end From 31bfe19d77c254e214c25f9efb7ecc90c1dfb8a6 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 26 Nov 2020 14:07:02 +0500 Subject: [PATCH 151/202] Move DomainUpdateConfirm from Que to ActiveJob --- app/jobs/application_job.rb | 2 ++ app/jobs/domain_update_confirm_job.rb | 7 ++++--- app/models/registrant_verification.rb | 4 ++-- test/jobs/domain_update_confirm_job_test.rb | 8 ++++---- 4 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 app/jobs/application_job.rb diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/jobs/domain_update_confirm_job.rb b/app/jobs/domain_update_confirm_job.rb index f3665f1e8..2025354e6 100644 --- a/app/jobs/domain_update_confirm_job.rb +++ b/app/jobs/domain_update_confirm_job.rb @@ -1,5 +1,7 @@ -class DomainUpdateConfirmJob < Que::Job - def run(domain_id, action, initiator = nil) +class DomainUpdateConfirmJob < ApplicationJob + queue_as :default + + def perform(domain_id, action, initiator = nil) ::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{action} by #{initiator}" # it's recommended to keep transaction against job table as short as possible. ActiveRecord::Base.transaction do @@ -27,7 +29,6 @@ class DomainUpdateConfirmJob < Que::Job domain.preclean_pendings domain.clean_pendings! end - destroy # it's best to destroy the job in the same transaction end end diff --git a/app/models/registrant_verification.rb b/app/models/registrant_verification.rb index 097f0cfa9..7e238cecc 100644 --- a/app/models/registrant_verification.rb +++ b/app/models/registrant_verification.rb @@ -18,13 +18,13 @@ class RegistrantVerification < ApplicationRecord def domain_registrant_change_confirm!(initiator) self.action_type = DOMAIN_REGISTRANT_CHANGE self.action = CONFIRMED - DomainUpdateConfirmJob.enqueue domain.id, CONFIRMED, initiator if save + DomainUpdateConfirmJob.perform_later domain.id, CONFIRMED, initiator if save end def domain_registrant_change_reject!(initiator) self.action_type = DOMAIN_REGISTRANT_CHANGE self.action = REJECTED - DomainUpdateConfirmJob.run domain.id, REJECTED, initiator if save + DomainUpdateConfirmJob.perform_later domain.id, REJECTED, initiator if save end def domain_registrant_delete_confirm!(initiator) diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index 9cca81eb7..f01e7c41e 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -20,7 +20,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase end def test_rejected_registrant_verification_notifies_registrar - DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED) + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) last_registrar_notification = @domain.registrar.notifications.last assert_equal(last_registrar_notification.attached_obj_id, @domain.id) @@ -28,7 +28,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase end def test_accepted_registrant_verification_notifies_registrar - DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED) + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::CONFIRMED) last_registrar_notification = @domain.registrar.notifications.last assert_equal(last_registrar_notification.attached_obj_id, @domain.id) @@ -44,7 +44,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase @domain.update(pending_json: @domain.pending_json) @domain.reload - DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED) + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::CONFIRMED) @domain.reload assert_equal @domain.registrant.code, @new_registrant.code @@ -58,7 +58,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase @domain.pending_json['frame'] = epp_xml @domain.update(pending_json: @domain.pending_json) - DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED) + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) @domain.reload assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION From 4b7cffbb572ea1e63e8d02962df994791770ef8b Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 26 Nov 2020 15:01:09 +0500 Subject: [PATCH 152/202] Move functionality from job to interactor --- .../domain_update_confirm_interaction/base.rb | 22 ++++++++++++ .../process_action.rb | 15 ++++++++ .../process_update_confirmed.rb | 20 +++++++++++ .../process_update_rejected.rb | 16 +++++++++ app/jobs/domain_update_confirm_job.rb | 36 +++---------------- 5 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 app/interactions/domain_update_confirm_interaction/base.rb create mode 100644 app/interactions/domain_update_confirm_interaction/process_action.rb create mode 100644 app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb create mode 100644 app/interactions/domain_update_confirm_interaction/process_update_rejected.rb diff --git a/app/interactions/domain_update_confirm_interaction/base.rb b/app/interactions/domain_update_confirm_interaction/base.rb new file mode 100644 index 000000000..18a96fc99 --- /dev/null +++ b/app/interactions/domain_update_confirm_interaction/base.rb @@ -0,0 +1,22 @@ +module DomainUpdateConfirmInteraction + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to confirm update' + string :action + string :initiator, + default: nil + + validates :domain, :action, presence: true + validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED, + RegistrantVerification::REJECTED] } + + def raise_errors!(domain) + if domain.errors.any? + message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" + throw message + end + end + end +end + diff --git a/app/interactions/domain_update_confirm_interaction/process_action.rb b/app/interactions/domain_update_confirm_interaction/process_action.rb new file mode 100644 index 000000000..9def919d1 --- /dev/null +++ b/app/interactions/domain_update_confirm_interaction/process_action.rb @@ -0,0 +1,15 @@ +module DomainUpdateConfirmInteraction + class ProcessAction < Base + def execute + ::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\ + " #{initiator}" + + case action + when RegistrantVerification::CONFIRMED + compose(ProcessUpdateConfirmed, inputs) + when RegistrantVerification::REJECTED + compose(ProcessUpdateRejected, inputs) + end + end + end +end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb new file mode 100644 index 000000000..bb235dcb9 --- /dev/null +++ b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb @@ -0,0 +1,20 @@ +module DomainUpdateConfirmInteraction + class ProcessUpdateConfirmed < Base + def execute + ActiveRecord::Base.transaction do + domain.is_admin = true + old_registrant = domain.registrant + domain.notify_registrar(:poll_pending_update_confirmed_by_registrant) + + domain.apply_pending_update! + raise_errors!(domain) + + domain.clean_pendings! + raise_errors!(domain) + RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm + end + end + + + end +end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb new file mode 100644 index 000000000..5875d17d5 --- /dev/null +++ b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb @@ -0,0 +1,16 @@ +module DomainUpdateConfirmInteraction + class ProcessUpdateRejected < Base + def execute + ActiveRecord::Base.transaction do + RegistrantChangeMailer.rejected(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_now + + domain.notify_registrar(:poll_pending_update_rejected_by_registrant) + + domain.preclean_pendings + domain.clean_pendings! + end + end + end +end diff --git a/app/jobs/domain_update_confirm_job.rb b/app/jobs/domain_update_confirm_job.rb index 2025354e6..8f1fd1688 100644 --- a/app/jobs/domain_update_confirm_job.rb +++ b/app/jobs/domain_update_confirm_job.rb @@ -2,37 +2,9 @@ class DomainUpdateConfirmJob < ApplicationJob queue_as :default def perform(domain_id, action, initiator = nil) - ::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{action} by #{initiator}" - # it's recommended to keep transaction against job table as short as possible. - ActiveRecord::Base.transaction do - domain = Epp::Domain.find(domain_id) - domain.is_admin = true - case action - when RegistrantVerification::CONFIRMED - old_registrant = domain.registrant - domain.notify_registrar(:poll_pending_update_confirmed_by_registrant) - raise_errors!(domain) - - domain.apply_pending_update! - raise_errors!(domain) - - domain.clean_pendings! - raise_errors!(domain) - RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm - when RegistrantVerification::REJECTED - RegistrantChangeMailer.rejected(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_now - - domain.notify_registrar(:poll_pending_update_rejected_by_registrant) - - domain.preclean_pendings - domain.clean_pendings! - end - end - end - - def raise_errors!(domain) - throw "domain #{domain.name} failed with errors #{domain.errors.full_messages}" if domain.errors.any? + domain = Epp::Domain.find(domain_id) + DomainUpdateConfirmInteraction::ProcessAction.run(domain: domain, + action: action, + initiator: initiator) end end From 8ef748fc7d61e34e784529722add638d1862d380 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 26 Nov 2020 15:21:22 +0500 Subject: [PATCH 153/202] Move apply_pending_update! method from model to interactor --- .codeclimate.yml | 3 ++ .../domain_update_confirm_interaction/base.rb | 17 +++++--- .../process_update_confirmed.rb | 42 ++++++++++++++++--- .../process_update_rejected.rb | 2 +- app/models/domain.rb | 3 ++ app/models/epp/domain.rb | 19 --------- 6 files changed, 56 insertions(+), 30 deletions(-) diff --git a/.codeclimate.yml b/.codeclimate.yml index 2bc90b200..d079d891f 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -20,6 +20,9 @@ plugins: channel: eslint-5 fixme: enabled: true + checks: + TODO: + enabled: false rubocop: enabled: true channel: rubocop-0-74 diff --git a/app/interactions/domain_update_confirm_interaction/base.rb b/app/interactions/domain_update_confirm_interaction/base.rb index 18a96fc99..d78f3a6a9 100644 --- a/app/interactions/domain_update_confirm_interaction/base.rb +++ b/app/interactions/domain_update_confirm_interaction/base.rb @@ -12,11 +12,18 @@ module DomainUpdateConfirmInteraction RegistrantVerification::REJECTED] } def raise_errors!(domain) - if domain.errors.any? - message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" - throw message - end + return unless domain.errors.any? + + message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" + throw message + end + + def notify_registrar(message_key) + domain.registrar.notifications.create!( + text: "#{I18n.t(message_key)}: #{domain.name}", + attached_obj_id: domain.id, + attached_obj_type: domain.class.to_s + ) end end end - diff --git a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb index bb235dcb9..43c053ba2 100644 --- a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb +++ b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb @@ -4,17 +4,49 @@ module DomainUpdateConfirmInteraction ActiveRecord::Base.transaction do domain.is_admin = true old_registrant = domain.registrant - domain.notify_registrar(:poll_pending_update_confirmed_by_registrant) + notify_registrar(:poll_pending_update_confirmed_by_registrant) - domain.apply_pending_update! - raise_errors!(domain) - - domain.clean_pendings! + apply_pending_update! raise_errors!(domain) RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm end end + def apply_pending_update! + preclean_pendings + update_domain + clean_pendings! + domain.save! + WhoisRecord.find_by(domain_id: domain.id).save # need to reload model + end + + def preclean_pendings + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def update_domain + user = ApiUser.find(domain.pending_json['current_user_id']) + frame = Nokogiri::XML(domain.pending_json['frame']) + domain.upid = user.registrar.id if user.registrar + domain.update(frame, user, false) + end + + def clean_pendings! + domain.up_date = Time.zone.now + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + domain.pending_json = {} + clear_statuses + end + + def clear_statuses + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) + domain.statuses.delete(DomainStatus::PENDING_UPDATE) + domain.statuses.delete(DomainStatus::PENDING_DELETE) + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end end end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb index 5875d17d5..09da14971 100644 --- a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb +++ b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb @@ -6,7 +6,7 @@ module DomainUpdateConfirmInteraction registrar: domain.registrar, registrant: domain.registrant).deliver_now - domain.notify_registrar(:poll_pending_update_rejected_by_registrant) + notify_registrar(:poll_pending_update_rejected_by_registrant) domain.preclean_pendings domain.clean_pendings! diff --git a/app/models/domain.rb b/app/models/domain.rb index dc7d86da8..d2f555977 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -327,6 +327,7 @@ class Domain < ApplicationRecord end def notify_registrar(message_key) + # TODO: To be deleted with DomainDeleteConfirm refactoring registrar.notifications.create!( text: "#{I18n.t(message_key)}: #{name}", attached_obj_id: id, @@ -335,11 +336,13 @@ class Domain < ApplicationRecord end def preclean_pendings + # TODO: To be deleted with refactoring self.registrant_verification_token = nil self.registrant_verification_asked_at = nil end def clean_pendings! + # TODO: To be deleted with refactoring preclean_pendings self.pending_json = {} statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index 7fb23a6e9..d8f5f2bb9 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -508,25 +508,6 @@ class Epp::Domain < Domain errors.empty? && super(at) end - def apply_pending_update! - preclean_pendings - user = ApiUser.find(pending_json['current_user_id']) - frame = Nokogiri::XML(pending_json['frame']) - - self.statuses.delete(DomainStatus::PENDING_UPDATE) - self.upid = user.registrar.id if user.registrar - self.up_date = Time.zone.now - - return unless update(frame, user, false) - clean_pendings! - - save! - - WhoisRecord.find_by(domain_id: id).save # need to reload model - - true - end - def apply_pending_delete! preclean_pendings statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) From 6f0b8b15a3c4994b1719c361c2e447ec5cf609c1 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 26 Nov 2020 16:13:50 +0500 Subject: [PATCH 154/202] Move clean pendings used from model to interactor base --- .../domain_update_confirm_interaction/base.rb | 20 ++++++++++++++++ .../process_update_confirmed.rb | 24 +++---------------- .../process_update_rejected.rb | 4 ++-- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/app/interactions/domain_update_confirm_interaction/base.rb b/app/interactions/domain_update_confirm_interaction/base.rb index d78f3a6a9..1a1f61b12 100644 --- a/app/interactions/domain_update_confirm_interaction/base.rb +++ b/app/interactions/domain_update_confirm_interaction/base.rb @@ -25,5 +25,25 @@ module DomainUpdateConfirmInteraction attached_obj_type: domain.class.to_s ) end + + def preclean_pendings + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def clean_pendings! + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + domain.pending_json = {} + clear_statuses + end + + def clear_statuses + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) + domain.statuses.delete(DomainStatus::PENDING_UPDATE) + domain.statuses.delete(DomainStatus::PENDING_DELETE) + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end end end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb index 43c053ba2..0e173c533 100644 --- a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb +++ b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb @@ -21,32 +21,14 @@ module DomainUpdateConfirmInteraction WhoisRecord.find_by(domain_id: domain.id).save # need to reload model end - def preclean_pendings - domain.registrant_verification_token = nil - domain.registrant_verification_asked_at = nil - end - + # rubocop:disable Metrics/AbcSize def update_domain user = ApiUser.find(domain.pending_json['current_user_id']) frame = Nokogiri::XML(domain.pending_json['frame']) domain.upid = user.registrar.id if user.registrar + domain.up_date = Time.zone.now domain.update(frame, user, false) end - - def clean_pendings! - domain.up_date = Time.zone.now - domain.registrant_verification_token = nil - domain.registrant_verification_asked_at = nil - domain.pending_json = {} - clear_statuses - end - - def clear_statuses - domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) - domain.statuses.delete(DomainStatus::PENDING_UPDATE) - domain.statuses.delete(DomainStatus::PENDING_DELETE) - domain.status_notes[DomainStatus::PENDING_UPDATE] = '' - domain.status_notes[DomainStatus::PENDING_DELETE] = '' - end + # rubocop:enable Metrics/AbcSize end end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb index 09da14971..390f8db49 100644 --- a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb +++ b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb @@ -8,8 +8,8 @@ module DomainUpdateConfirmInteraction notify_registrar(:poll_pending_update_rejected_by_registrant) - domain.preclean_pendings - domain.clean_pendings! + preclean_pendings + clean_pendings! end end end From 42012863e23a8e2b3a31ad08a84aa1d325f640f6 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 30 Nov 2020 17:53:36 +0500 Subject: [PATCH 155/202] Move interactor to the Domains namespace --- .../domain_update_confirm_interaction/base.rb | 49 ------------------ .../process_action.rb | 15 ------ .../process_update_confirmed.rb | 34 ------------- .../process_update_rejected.rb | 16 ------ .../domains/update_confirm/base.rb | 51 +++++++++++++++++++ .../domains/update_confirm/process_action.rb | 17 +++++++ .../process_update_confirmed.rb | 36 +++++++++++++ .../update_confirm/process_update_rejected.rb | 18 +++++++ app/jobs/domain_update_confirm_job.rb | 6 +-- 9 files changed, 125 insertions(+), 117 deletions(-) delete mode 100644 app/interactions/domain_update_confirm_interaction/base.rb delete mode 100644 app/interactions/domain_update_confirm_interaction/process_action.rb delete mode 100644 app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb delete mode 100644 app/interactions/domain_update_confirm_interaction/process_update_rejected.rb create mode 100644 app/interactions/domains/update_confirm/base.rb create mode 100644 app/interactions/domains/update_confirm/process_action.rb create mode 100644 app/interactions/domains/update_confirm/process_update_confirmed.rb create mode 100644 app/interactions/domains/update_confirm/process_update_rejected.rb diff --git a/app/interactions/domain_update_confirm_interaction/base.rb b/app/interactions/domain_update_confirm_interaction/base.rb deleted file mode 100644 index 1a1f61b12..000000000 --- a/app/interactions/domain_update_confirm_interaction/base.rb +++ /dev/null @@ -1,49 +0,0 @@ -module DomainUpdateConfirmInteraction - class Base < ActiveInteraction::Base - object :domain, - class: Domain, - description: 'Domain to confirm update' - string :action - string :initiator, - default: nil - - validates :domain, :action, presence: true - validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED, - RegistrantVerification::REJECTED] } - - def raise_errors!(domain) - return unless domain.errors.any? - - message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" - throw message - end - - def notify_registrar(message_key) - domain.registrar.notifications.create!( - text: "#{I18n.t(message_key)}: #{domain.name}", - attached_obj_id: domain.id, - attached_obj_type: domain.class.to_s - ) - end - - def preclean_pendings - domain.registrant_verification_token = nil - domain.registrant_verification_asked_at = nil - end - - def clean_pendings! - domain.registrant_verification_token = nil - domain.registrant_verification_asked_at = nil - domain.pending_json = {} - clear_statuses - end - - def clear_statuses - domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) - domain.statuses.delete(DomainStatus::PENDING_UPDATE) - domain.statuses.delete(DomainStatus::PENDING_DELETE) - domain.status_notes[DomainStatus::PENDING_UPDATE] = '' - domain.status_notes[DomainStatus::PENDING_DELETE] = '' - end - end -end diff --git a/app/interactions/domain_update_confirm_interaction/process_action.rb b/app/interactions/domain_update_confirm_interaction/process_action.rb deleted file mode 100644 index 9def919d1..000000000 --- a/app/interactions/domain_update_confirm_interaction/process_action.rb +++ /dev/null @@ -1,15 +0,0 @@ -module DomainUpdateConfirmInteraction - class ProcessAction < Base - def execute - ::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\ - " #{initiator}" - - case action - when RegistrantVerification::CONFIRMED - compose(ProcessUpdateConfirmed, inputs) - when RegistrantVerification::REJECTED - compose(ProcessUpdateRejected, inputs) - end - end - end -end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb b/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb deleted file mode 100644 index 0e173c533..000000000 --- a/app/interactions/domain_update_confirm_interaction/process_update_confirmed.rb +++ /dev/null @@ -1,34 +0,0 @@ -module DomainUpdateConfirmInteraction - class ProcessUpdateConfirmed < Base - def execute - ActiveRecord::Base.transaction do - domain.is_admin = true - old_registrant = domain.registrant - notify_registrar(:poll_pending_update_confirmed_by_registrant) - - apply_pending_update! - raise_errors!(domain) - RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm - end - end - - def apply_pending_update! - preclean_pendings - update_domain - clean_pendings! - domain.save! - - WhoisRecord.find_by(domain_id: domain.id).save # need to reload model - end - - # rubocop:disable Metrics/AbcSize - def update_domain - user = ApiUser.find(domain.pending_json['current_user_id']) - frame = Nokogiri::XML(domain.pending_json['frame']) - domain.upid = user.registrar.id if user.registrar - domain.up_date = Time.zone.now - domain.update(frame, user, false) - end - # rubocop:enable Metrics/AbcSize - end -end diff --git a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb b/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb deleted file mode 100644 index 390f8db49..000000000 --- a/app/interactions/domain_update_confirm_interaction/process_update_rejected.rb +++ /dev/null @@ -1,16 +0,0 @@ -module DomainUpdateConfirmInteraction - class ProcessUpdateRejected < Base - def execute - ActiveRecord::Base.transaction do - RegistrantChangeMailer.rejected(domain: domain, - registrar: domain.registrar, - registrant: domain.registrant).deliver_now - - notify_registrar(:poll_pending_update_rejected_by_registrant) - - preclean_pendings - clean_pendings! - end - end - end -end diff --git a/app/interactions/domains/update_confirm/base.rb b/app/interactions/domains/update_confirm/base.rb new file mode 100644 index 000000000..ab5584c03 --- /dev/null +++ b/app/interactions/domains/update_confirm/base.rb @@ -0,0 +1,51 @@ +module Domains + module UpdateConfirm + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to confirm update' + string :action + string :initiator, + default: nil + + validates :domain, :action, presence: true + validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED, + RegistrantVerification::REJECTED] } + + def raise_errors!(domain) + return unless domain.errors.any? + + message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" + throw message + end + + def notify_registrar(message_key) + domain.registrar.notifications.create!( + text: "#{I18n.t(message_key)}: #{domain.name}", + attached_obj_id: domain.id, + attached_obj_type: domain.class.to_s + ) + end + + def preclean_pendings + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def clean_pendings! + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + domain.pending_json = {} + clear_statuses + end + + def clear_statuses + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) + domain.statuses.delete(DomainStatus::PENDING_UPDATE) + domain.statuses.delete(DomainStatus::PENDING_DELETE) + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end + end + end +end diff --git a/app/interactions/domains/update_confirm/process_action.rb b/app/interactions/domains/update_confirm/process_action.rb new file mode 100644 index 000000000..6ef8d0fe6 --- /dev/null +++ b/app/interactions/domains/update_confirm/process_action.rb @@ -0,0 +1,17 @@ +module Domains + module UpdateConfirm + class ProcessAction < Base + def execute + ::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\ + " #{initiator}" + + case action + when RegistrantVerification::CONFIRMED + compose(ProcessUpdateConfirmed, inputs) + when RegistrantVerification::REJECTED + compose(ProcessUpdateRejected, inputs) + end + 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 new file mode 100644 index 000000000..0402994dd --- /dev/null +++ b/app/interactions/domains/update_confirm/process_update_confirmed.rb @@ -0,0 +1,36 @@ +module Domains + module UpdateConfirm + class ProcessUpdateConfirmed < Base + def execute + ActiveRecord::Base.transaction do + domain.is_admin = true + old_registrant = domain.registrant + notify_registrar(:poll_pending_update_confirmed_by_registrant) + + apply_pending_update! + raise_errors!(domain) + RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm + end + end + + def apply_pending_update! + preclean_pendings + update_domain + clean_pendings! + domain.save! + + WhoisRecord.find_by(domain_id: domain.id).save # need to reload model + end + + # rubocop:disable Metrics/AbcSize + def update_domain + user = ApiUser.find(domain.pending_json['current_user_id']) + frame = Nokogiri::XML(domain.pending_json['frame']) + domain.upid = user.registrar.id if user.registrar + domain.up_date = Time.zone.now + domain.update(frame, user, false) + end + # rubocop:enable Metrics/AbcSize + end + end +end diff --git a/app/interactions/domains/update_confirm/process_update_rejected.rb b/app/interactions/domains/update_confirm/process_update_rejected.rb new file mode 100644 index 000000000..1d7b75b0e --- /dev/null +++ b/app/interactions/domains/update_confirm/process_update_rejected.rb @@ -0,0 +1,18 @@ +module Domains + module UpdateConfirm + class ProcessUpdateRejected < Base + def execute + ActiveRecord::Base.transaction do + RegistrantChangeMailer.rejected(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_now + + notify_registrar(:poll_pending_update_rejected_by_registrant) + + preclean_pendings + clean_pendings! + end + end + end + end +end diff --git a/app/jobs/domain_update_confirm_job.rb b/app/jobs/domain_update_confirm_job.rb index 8f1fd1688..403318ca6 100644 --- a/app/jobs/domain_update_confirm_job.rb +++ b/app/jobs/domain_update_confirm_job.rb @@ -3,8 +3,8 @@ class DomainUpdateConfirmJob < ApplicationJob def perform(domain_id, action, initiator = nil) domain = Epp::Domain.find(domain_id) - DomainUpdateConfirmInteraction::ProcessAction.run(domain: domain, - action: action, - initiator: initiator) + Domains::UpdateConfirm::ProcessAction.run(domain: domain, + action: action, + initiator: initiator) end end From a0bc60ac74a1b29e5f18274f8d8cdd9896d8fb80 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 18:28:34 +0500 Subject: [PATCH 156/202] Fix rejecting changes clearance --- .../domains/update_confirm/process_update_rejected.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/interactions/domains/update_confirm/process_update_rejected.rb b/app/interactions/domains/update_confirm/process_update_rejected.rb index 1d7b75b0e..19969d970 100644 --- a/app/interactions/domains/update_confirm/process_update_rejected.rb +++ b/app/interactions/domains/update_confirm/process_update_rejected.rb @@ -11,6 +11,7 @@ module Domains preclean_pendings clean_pendings! + domain.save! end end end From 6ab08dbd673e2b9d9e8801ec23ff58e903b6d155 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 2 Dec 2020 13:51:49 +0500 Subject: [PATCH 157/202] Fix rejecting updates --- .../process_update_confirmed.rb | 2 +- .../update_confirm/process_update_rejected.rb | 2 +- test/jobs/domain_update_confirm_job_test.rb | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/interactions/domains/update_confirm/process_update_confirmed.rb b/app/interactions/domains/update_confirm/process_update_confirmed.rb index 0402994dd..a4ceb1d3e 100644 --- a/app/interactions/domains/update_confirm/process_update_confirmed.rb +++ b/app/interactions/domains/update_confirm/process_update_confirmed.rb @@ -17,7 +17,7 @@ module Domains preclean_pendings update_domain clean_pendings! - domain.save! + domain.save(validate: false) WhoisRecord.find_by(domain_id: domain.id).save # need to reload model end diff --git a/app/interactions/domains/update_confirm/process_update_rejected.rb b/app/interactions/domains/update_confirm/process_update_rejected.rb index 19969d970..5ce818e66 100644 --- a/app/interactions/domains/update_confirm/process_update_rejected.rb +++ b/app/interactions/domains/update_confirm/process_update_rejected.rb @@ -11,7 +11,7 @@ module Domains preclean_pendings clean_pendings! - domain.save! + domain.save(validate: false) end end end diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index f01e7c41e..38aaa5339 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -64,4 +64,24 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE end + + def test_clears_pending_update_and_inactive_after_denial + epp_xml = "\n\n \n \n \n #{@domain.name}\n" \ + " \n #{@new_registrant.code}\n \n \n \n \n \n" \ + " \n #{@legal_doc_path}\n \n" \ + " \n 20alla-1594199756\n \n\n" + @domain.pending_json['frame'] = epp_xml + @domain.update(pending_json: @domain.pending_json) + @domain.update(statuses: [DomainStatus::INACTIVE, DomainStatus::PENDING_UPDATE]) + @domain.nameservers.destroy_all + @domain.reload + + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) + @domain.reload + + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE + assert_not @domain.statuses.include? DomainStatus::PENDING_UPDATE + assert @domain.statuses.include? DomainStatus::INACTIVE + end end From 252d39464af5f299d2a9ad1fa101a4564ac67659 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 3 Dec 2020 13:39:58 +0500 Subject: [PATCH 158/202] Fix updating procedure --- .../domains/update_confirm/base.rb | 2 ++ .../update_confirm/process_update_confirmed.rb | 2 -- .../update_confirm/process_update_rejected.rb | 1 - test/jobs/domain_update_confirm_job_test.rb | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/interactions/domains/update_confirm/base.rb b/app/interactions/domains/update_confirm/base.rb index ab5584c03..0e1fa81d3 100644 --- a/app/interactions/domains/update_confirm/base.rb +++ b/app/interactions/domains/update_confirm/base.rb @@ -33,10 +33,12 @@ module Domains end def clean_pendings! + domain.is_admin = true domain.registrant_verification_token = nil domain.registrant_verification_asked_at = nil domain.pending_json = {} clear_statuses + domain.save end def clear_statuses diff --git a/app/interactions/domains/update_confirm/process_update_confirmed.rb b/app/interactions/domains/update_confirm/process_update_confirmed.rb index a4ceb1d3e..cb69d042e 100644 --- a/app/interactions/domains/update_confirm/process_update_confirmed.rb +++ b/app/interactions/domains/update_confirm/process_update_confirmed.rb @@ -3,7 +3,6 @@ module Domains class ProcessUpdateConfirmed < Base def execute ActiveRecord::Base.transaction do - domain.is_admin = true old_registrant = domain.registrant notify_registrar(:poll_pending_update_confirmed_by_registrant) @@ -17,7 +16,6 @@ module Domains preclean_pendings update_domain clean_pendings! - domain.save(validate: false) WhoisRecord.find_by(domain_id: domain.id).save # need to reload model end diff --git a/app/interactions/domains/update_confirm/process_update_rejected.rb b/app/interactions/domains/update_confirm/process_update_rejected.rb index 5ce818e66..1d7b75b0e 100644 --- a/app/interactions/domains/update_confirm/process_update_rejected.rb +++ b/app/interactions/domains/update_confirm/process_update_rejected.rb @@ -11,7 +11,6 @@ module Domains preclean_pendings clean_pendings! - domain.save(validate: false) end end end diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index 38aaa5339..afffae157 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -84,4 +84,22 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase assert_not @domain.statuses.include? DomainStatus::PENDING_UPDATE assert @domain.statuses.include? DomainStatus::INACTIVE end + + def test_clears_pending_update_and_sets_ok_after_denial + epp_xml = "\n\n \n \n \n #{@domain.name}\n" \ + " \n #{@new_registrant.code}\n \n \n \n \n \n" \ + " \n #{@legal_doc_path}\n \n" \ + " \n 20alla-1594199756\n \n\n" + @domain.pending_json['frame'] = epp_xml + @domain.update(pending_json: @domain.pending_json) + @domain.update(statuses: [DomainStatus::OK, DomainStatus::PENDING_UPDATE]) + + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) + @domain.reload + + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE + assert_not @domain.statuses.include? DomainStatus::PENDING_UPDATE + assert @domain.statuses.include? DomainStatus::OK + end end From 7a90ef528abb14d0df62b956b6738a26d3593c5e Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 4 Dec 2020 13:57:53 +0500 Subject: [PATCH 159/202] Add a couple of domain status tests --- test/jobs/domain_update_confirm_job_test.rb | 39 ++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index afffae157..ded0d3d8a 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -48,6 +48,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase @domain.reload assert_equal @domain.registrant.code, @new_registrant.code + assert @domain.statuses.include? DomainStatus::OK end def test_clears_pending_update_after_denial @@ -65,6 +66,42 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE end + def test_protects_statuses_after_denial + epp_xml = "\n\n \n \n \n #{@domain.name}\n" \ + " \n #{@new_registrant.code}\n \n \n \n \n \n" \ + " \n #{@legal_doc_path}\n \n" \ + " \n 20alla-1594199756\n \n\n" + @domain.pending_json['frame'] = epp_xml + @domain.update(pending_json: @domain.pending_json) + @domain.update(statuses: [DomainStatus::DELETE_CANDIDATE, DomainStatus::DISPUTED]) + + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) + @domain.reload + + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE + assert @domain.statuses.include? DomainStatus::DELETE_CANDIDATE + assert @domain.statuses.include? DomainStatus::DISPUTED + end + + def test_protects_statuses_after_confirm + epp_xml = "\n\n \n \n \n #{@domain.name}\n" \ + " \n #{@new_registrant.code}\n \n \n \n \n \n" \ + " \n #{@legal_doc_path}\n \n" \ + " \n 20alla-1594199756\n \n\n" + @domain.pending_json['frame'] = epp_xml + @domain.update(pending_json: @domain.pending_json) + @domain.update(statuses: [DomainStatus::DELETE_CANDIDATE, DomainStatus::DISPUTED]) + + DomainUpdateConfirmJob.perform_now(@domain.id, RegistrantVerification::CONFIRMED) + @domain.reload + + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE_CONFIRMATION + assert_not @domain.statuses.include? DomainStatus::PENDING_DELETE + assert @domain.statuses.include? DomainStatus::DELETE_CANDIDATE + assert @domain.statuses.include? DomainStatus::DISPUTED + end + def test_clears_pending_update_and_inactive_after_denial epp_xml = "\n\n \n \n \n #{@domain.name}\n" \ " \n #{@new_registrant.code}\n \n \n \n \n \n" \ @@ -72,7 +109,7 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase " \n 20alla-1594199756\n \n\n" @domain.pending_json['frame'] = epp_xml @domain.update(pending_json: @domain.pending_json) - @domain.update(statuses: [DomainStatus::INACTIVE, DomainStatus::PENDING_UPDATE]) + @domain.update(statuses: [DomainStatus::PENDING_UPDATE]) @domain.nameservers.destroy_all @domain.reload From e8fa79304f7e1b34846d74989deac1d753b6bbf7 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 15:23:23 +0500 Subject: [PATCH 160/202] Move DomainCron#clean_expired_pendings to interactor --- .../domains/expired_pendings/base.rb | 10 ++++ .../domains/expired_pendings/clean_all.rb | 35 +++++++++++ .../domains/expired_pendings/process_clean.rb | 60 +++++++++++++++++++ app/models/domain_cron.rb | 32 +--------- test/models/domain_cron_test.rb | 8 ++- 5 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 app/interactions/domains/expired_pendings/base.rb create mode 100644 app/interactions/domains/expired_pendings/clean_all.rb create mode 100644 app/interactions/domains/expired_pendings/process_clean.rb diff --git a/app/interactions/domains/expired_pendings/base.rb b/app/interactions/domains/expired_pendings/base.rb new file mode 100644 index 000000000..7faa32050 --- /dev/null +++ b/app/interactions/domains/expired_pendings/base.rb @@ -0,0 +1,10 @@ +module Domains + module ExpiredPendings + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/expired_pendings/clean_all.rb b/app/interactions/domains/expired_pendings/clean_all.rb new file mode 100644 index 000000000..1dbab266c --- /dev/null +++ b/app/interactions/domains/expired_pendings/clean_all.rb @@ -0,0 +1,35 @@ +module Domains + module ExpiredPendings + class CleanAll < Base + def execute + to_stdout('Clean expired domain pendings') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + + count = 0 + expired_pending_domains.each do |domain| + log_error(domain) && next unless need_to_be_cleared?(domain) + count += 1 + Domains::ExpiredPendings::ProcessClean.run(domain: domain) + end + to_stdout("Successfully cancelled #{count} domain pendings") + end + + private + + def need_to_be_cleared?(domain) + domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation? + end + + def log_error(domain) + to_stdout("ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, "\ + 'but no pendingDelete/pendingUpdate state present!') + end + + def expired_pending_domains + expire_at = Setting.expire_pending_confirmation.hours.ago + Domain.where('registrant_verification_asked_at <= ?', expire_at) + end + end + end +end diff --git a/app/interactions/domains/expired_pendings/process_clean.rb b/app/interactions/domains/expired_pendings/process_clean.rb new file mode 100644 index 000000000..c6277c3c5 --- /dev/null +++ b/app/interactions/domains/expired_pendings/process_clean.rb @@ -0,0 +1,60 @@ +module Domains + module ExpiredPendings + class ProcessClean < Base + object :domain, + class: Domain + + def execute + check_notify + clean_pendings + + to_stdout("DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})") + UpdateWhoisRecordJob.enqueue domain.name, 'domain' + end + + private + + def notify_pending_update + RegistrantChangeMailer.expired(domain: domain, + registrar: domain.registrar, + registrant: domain.registrant).deliver_later + end + + def notify_pending_delete + DomainDeleteMailer.expired(domain).deliver_later + end + + def clean_pendings + clean_verification_data + domain.pending_json = {} + clean_statuses + domain.save + end + + def statuses_to_clean + [DomainStatus::PENDING_DELETE_CONFIRMATION, + DomainStatus::PENDING_UPDATE, + DomainStatus::PENDING_DELETE] + end + + def clean_statuses + domain.statuses = domain.statuses - statuses_to_clean + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end + + def clean_verification_data + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def check_notify + notify_pending_update if domain.pending_update? + + return unless domain.pending_delete? || domain.pending_delete_confirmation? + + notify_pending_delete + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index d09141d52..77de7062a 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -1,36 +1,6 @@ class DomainCron def self.clean_expired_pendings - STDOUT << "#{Time.zone.now.utc} - Clean expired domain pendings\n" unless Rails.env.test? - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - expire_at = Setting.expire_pending_confirmation.hours.ago - count = 0 - expired_pending_domains = Domain.where('registrant_verification_asked_at <= ?', expire_at) - expired_pending_domains.each do |domain| - unless domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation? - msg = "#{Time.zone.now.utc} - ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, " \ - "but no pendingDelete/pendingUpdate state present!\n" - STDOUT << msg unless Rails.env.test? - next - end - count += 1 - if domain.pending_update? - RegistrantChangeExpiredEmailJob.enqueue(domain.id) - end - if domain.pending_delete? || domain.pending_delete_confirmation? - DomainDeleteMailer.expired(domain).deliver_now - end - - domain.preclean_pendings - domain.clean_pendings! - - unless Rails.env.test? - STDOUT << "#{Time.zone.now.utc} DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})\n" - end - UpdateWhoisRecordJob.enqueue domain.name, 'domain' - end - STDOUT << "#{Time.zone.now.utc} - Successfully cancelled #{count} domain pendings\n" unless Rails.env.test? - count + Domains::ExpiredPendings::CleanAll.run! end def self.start_expire_period diff --git a/test/models/domain_cron_test.rb b/test/models/domain_cron_test.rb index 0224b1a61..c417df04f 100644 --- a/test/models/domain_cron_test.rb +++ b/test/models/domain_cron_test.rb @@ -19,7 +19,9 @@ class DomainCronTest < ActiveSupport::TestCase registrant_verification_token: 'test', statuses: [DomainStatus::PENDING_DELETE_CONFIRMATION]) - DomainCron.clean_expired_pendings + perform_enqueued_jobs do + DomainCron.clean_expired_pendings + end assert_emails 1 end @@ -84,7 +86,9 @@ class DomainCronTest < ActiveSupport::TestCase assert @domain.pending_update? @domain.reload - DomainCron.clean_expired_pendings + perform_enqueued_jobs do + DomainCron.clean_expired_pendings + end @domain.reload assert_not @domain.pending_update? From 849010b118addcba6057caef1d2b7685e2568fef Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 16:44:31 +0500 Subject: [PATCH 161/202] Move #process_expired to interactor --- .../domains/expire_period/base.rb | 10 +++++++ .../domains/expire_period/process_expired.rb | 28 +++++++++++++++++++ .../domains/expire_period/start.rb | 19 +++++++++++++ app/models/domain.rb | 6 ---- app/models/domain_cron.rb | 22 +-------------- 5 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 app/interactions/domains/expire_period/base.rb create mode 100644 app/interactions/domains/expire_period/process_expired.rb create mode 100644 app/interactions/domains/expire_period/start.rb diff --git a/app/interactions/domains/expire_period/base.rb b/app/interactions/domains/expire_period/base.rb new file mode 100644 index 000000000..eab8171bf --- /dev/null +++ b/app/interactions/domains/expire_period/base.rb @@ -0,0 +1,10 @@ +module Domains + module ExpirePeriod + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/expire_period/process_expired.rb b/app/interactions/domains/expire_period/process_expired.rb new file mode 100644 index 000000000..595de1228 --- /dev/null +++ b/app/interactions/domains/expire_period/process_expired.rb @@ -0,0 +1,28 @@ +module Domains + module ExpirePeriod + class ProcessExpired < Base + object :domain, + class: Domain, + description: 'Domain to set expiration' + + def execute + set_graceful_expired + to_stdout("start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}") + + saved = domain.save(validate: false) + + DomainExpireEmailJob.enqueue(domain.id, run_at: send_time) if saved + end + + def set_graceful_expired + domain.outzone_at = domain.expire_time + Domain.expire_warning_period + domain.delete_date = domain.outzone_at + Domain.redemption_grace_period + domain.statuses |= [DomainStatus::EXPIRED] + end + + def send_time + domain.valid_to + Setting.expiration_reminder_mail.to_i.days + end + end + end +end diff --git a/app/interactions/domains/expire_period/start.rb b/app/interactions/domains/expire_period/start.rb new file mode 100644 index 000000000..1ed2342f1 --- /dev/null +++ b/app/interactions/domains/expire_period/start.rb @@ -0,0 +1,19 @@ +module Domains + module ExpirePeriod + class Start < Base + def execute + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + count = 0 + + Domain.expired.each do |domain| + next unless domain.expirable? + + count += 1 + Domains::ExpirePeriod::ProcessExpired.run(domain: domain) + end + + to_stdout("Successfully expired #{count}") + end + end + end +end diff --git a/app/models/domain.rb b/app/models/domain.rb index dc7d86da8..33d2dcd58 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -482,12 +482,6 @@ class Domain < ApplicationRecord Registrant.find_by(id: pending_json['new_registrant_id']) end - def set_graceful_expired - self.outzone_at = expire_time + self.class.expire_warning_period - self.delete_date = outzone_at + self.class.redemption_grace_period - self.statuses |= [DomainStatus::EXPIRED] - end - def pending_update? statuses.include?(DomainStatus::PENDING_UPDATE) end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index 77de7062a..cd063f901 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -4,27 +4,7 @@ class DomainCron end def self.start_expire_period - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - domains = Domain.expired - marked = 0 - real = 0 - - domains.each do |domain| - next unless domain.expirable? - real += 1 - domain.set_graceful_expired - STDOUT << "#{Time.zone.now.utc} DomainCron.start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test? - - send_time = domain.valid_to + Setting.expiration_reminder_mail.to_i.days - saved = domain.save(validate: false) - - if saved - DomainExpireEmailJob.enqueue(domain.id, run_at: send_time) - marked += 1 - end - end - - STDOUT << "#{Time.zone.now.utc} - Successfully expired #{marked} of #{real} domains\n" unless Rails.env.test? + Domains::ExpirePeriod::Start.run! end def self.start_redemption_grace_period From d17e26c28b81e7ea9dc75d1fecd19ed14df1f623 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 1 Dec 2020 17:04:19 +0500 Subject: [PATCH 162/202] Move #redemption_grace_period to interactor --- .../domains/redemption_grace_period/base.rb | 10 ++++++++++ .../process_grace_period.rb | 20 +++++++++++++++++++ .../domains/redemption_grace_period/start.rb | 20 +++++++++++++++++++ app/models/domain_cron.rb | 19 +----------------- 4 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 app/interactions/domains/redemption_grace_period/base.rb create mode 100644 app/interactions/domains/redemption_grace_period/process_grace_period.rb create mode 100644 app/interactions/domains/redemption_grace_period/start.rb diff --git a/app/interactions/domains/redemption_grace_period/base.rb b/app/interactions/domains/redemption_grace_period/base.rb new file mode 100644 index 000000000..5d1ede289 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/base.rb @@ -0,0 +1,10 @@ +module Domains + module RedemptionGracePeriod + class Base < ActiveInteraction::Base + def to_stdout(message) + time = Time.zone.now.utc + STDOUT << "#{time} - #{message}\n" unless Rails.env.test? + end + end + end +end diff --git a/app/interactions/domains/redemption_grace_period/process_grace_period.rb b/app/interactions/domains/redemption_grace_period/process_grace_period.rb new file mode 100644 index 000000000..0f120a996 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/process_grace_period.rb @@ -0,0 +1,20 @@ +module Domains + module RedemptionGracePeriod + class ProcessGracePeriod < Base + object :domain, + class: Domain + + def execute + domain.statuses << DomainStatus::SERVER_HOLD + to_stdout(process_msg) + domain.save(validate: false) + end + + private + + def process_msg + "start_redemption_grace_period: #{domain.id} (#{domain.name}) #{domain.changes}" + end + end + end +end diff --git a/app/interactions/domains/redemption_grace_period/start.rb b/app/interactions/domains/redemption_grace_period/start.rb new file mode 100644 index 000000000..ef7f42e58 --- /dev/null +++ b/app/interactions/domains/redemption_grace_period/start.rb @@ -0,0 +1,20 @@ +module Domains + module RedemptionGracePeriod + class Start < Base + def execute + to_stdout('Setting server_hold to domains') + + ::PaperTrail.request.whodunnit = "cron - #{self.class.name}" + count = 0 + + Domain.outzone_candidates.each do |domain| + next unless domain.server_holdable? + + count += 1 + Domains::RedemptionGracePeriod::ProcessGracePeriod.run(domain: domain) + end + to_stdout("Successfully set server_hold to #{count} of domains") + end + end + end +end diff --git a/app/models/domain_cron.rb b/app/models/domain_cron.rb index cd063f901..e936c29e6 100644 --- a/app/models/domain_cron.rb +++ b/app/models/domain_cron.rb @@ -8,24 +8,7 @@ class DomainCron end def self.start_redemption_grace_period - STDOUT << "#{Time.zone.now.utc} - Setting server_hold to domains\n" unless Rails.env.test? - - ::PaperTrail.request.whodunnit = "cron - #{__method__}" - - domains = Domain.outzone_candidates - marked = 0 - real = 0 - - domains.each do |domain| - next unless domain.server_holdable? - real += 1 - domain.statuses << DomainStatus::SERVER_HOLD - STDOUT << "#{Time.zone.now.utc} DomainCron.start_redemption_grace_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test? - domain.save(validate: false) and marked += 1 - end - - STDOUT << "#{Time.zone.now.utc} - Successfully set server_hold to #{marked} of #{real} domains\n" unless Rails.env.test? - marked + Domains::RedemptionGracePeriod::Start.run! end def self.start_client_hold From 167b37c61fb4aefae70cbf7e3c1c281322f094ba Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 2 Dec 2020 14:31:37 +0500 Subject: [PATCH 163/202] Add tests for DOmainCrone#expire_period --- test/interactions/expire_period/start_test.rb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/interactions/expire_period/start_test.rb diff --git a/test/interactions/expire_period/start_test.rb b/test/interactions/expire_period/start_test.rb new file mode 100644 index 000000000..168b255bb --- /dev/null +++ b/test/interactions/expire_period/start_test.rb @@ -0,0 +1,28 @@ +require 'test_helper' + +class StartTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + setup do + @domain = domains(:shop) + @domain.update(expire_time: Time.zone.now - 1.day) + ActionMailer::Base.deliveries.clear + end + + def test_sets_expired + job_count = lambda do + QueJob.where("args->>0 = '#{@domain.id}'", job_class: DomainExpireEmailJob.name).count + end + + assert_difference job_count, 1 do + perform_enqueued_jobs do + DomainCron.start_expire_period + end + end + + @domain.reload + assert @domain.statuses.include?(DomainStatus::EXPIRED) + assert_equal @domain.outzone_at, @domain.expire_time + Domain.expire_warning_period + assert_equal @domain.delete_date, (@domain.outzone_at + Domain.redemption_grace_period).to_date + end +end From ac3860bbd8fc66b7eaa7ba2254516a0482642bad Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 2 Dec 2020 14:54:37 +0500 Subject: [PATCH 164/202] Add test for redemption grace period --- .../redemption_grace_period/start_test.rb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/interactions/redemption_grace_period/start_test.rb diff --git a/test/interactions/redemption_grace_period/start_test.rb b/test/interactions/redemption_grace_period/start_test.rb new file mode 100644 index 000000000..8958030f0 --- /dev/null +++ b/test/interactions/redemption_grace_period/start_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class StartTest < ActiveSupport::TestCase + + setup do + @domain = domains(:shop) + @domain.update(outzone_time: Time.zone.now - 1.day) + end + + def test_sets_server_hold + DomainCron.start_redemption_grace_period + + @domain.reload + assert @domain.statuses.include?(DomainStatus::SERVER_HOLD) + end + + def test_doesnt_sets_server_hold_if_not_outzone + @domain.update(outzone_time: nil) + @domain.reload + DomainCron.start_redemption_grace_period + + @domain.reload + assert_not @domain.statuses.include?(DomainStatus::SERVER_HOLD) + end +end From ccf91d306abdadced826e6afb09d6b60dbfa9e5f Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 4 Dec 2020 17:48:42 +0500 Subject: [PATCH 165/202] Rename email sending interactor --- .../{delete_confirm => delete_confirm_email}/send_request.rb | 2 +- app/models/domain.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/interactions/domains/{delete_confirm => delete_confirm_email}/send_request.rb (96%) diff --git a/app/interactions/domains/delete_confirm/send_request.rb b/app/interactions/domains/delete_confirm_email/send_request.rb similarity index 96% rename from app/interactions/domains/delete_confirm/send_request.rb rename to app/interactions/domains/delete_confirm_email/send_request.rb index 91afaefb8..7070423a6 100644 --- a/app/interactions/domains/delete_confirm/send_request.rb +++ b/app/interactions/domains/delete_confirm_email/send_request.rb @@ -1,5 +1,5 @@ module Domains - module DeleteConfirm + module DeleteConfirmEmail class SendRequest < ActiveInteraction::Base object :domain, class: Domain, diff --git a/app/models/domain.rb b/app/models/domain.rb index dc7d86da8..ee3481bb2 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -418,7 +418,7 @@ class Domain < ApplicationRecord pending_delete_confirmation! save(validate: false) # should check if this did succeed - Domains::DeleteConfirm::SendRequest.run(domain: self) + Domains::DeleteConfirmEmail::SendRequest.run(domain: self) end def cancel_pending_delete From f6a7a08b2433b20b1c131f7bc307b52a9e98b580 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Fri, 4 Dec 2020 18:09:11 +0500 Subject: [PATCH 166/202] Move job to interactor --- .../domains/delete_confirm/base.rb | 23 ++++++++++ .../domains/delete_confirm/process_action.rb | 17 ++++++++ .../process_delete_confirmed.rb | 11 +++++ .../delete_confirm/process_delete_rejected.rb | 34 +++++++++++++++ app/jobs/application_job.rb | 2 + app/jobs/domain_delete_confirm_job.rb | 42 ++++--------------- app/models/registrant_verification.rb | 4 +- test/jobs/domain_delete_confirm_job_test.rb | 8 ++-- .../domains/domain_delete_confirms_test.rb | 9 +++- 9 files changed, 107 insertions(+), 43 deletions(-) create mode 100644 app/interactions/domains/delete_confirm/base.rb create mode 100644 app/interactions/domains/delete_confirm/process_action.rb create mode 100644 app/interactions/domains/delete_confirm/process_delete_confirmed.rb create mode 100644 app/interactions/domains/delete_confirm/process_delete_rejected.rb create mode 100644 app/jobs/application_job.rb diff --git a/app/interactions/domains/delete_confirm/base.rb b/app/interactions/domains/delete_confirm/base.rb new file mode 100644 index 000000000..d1d45f006 --- /dev/null +++ b/app/interactions/domains/delete_confirm/base.rb @@ -0,0 +1,23 @@ +module Domains + module DeleteConfirm + class Base < ActiveInteraction::Base + object :domain, + class: Domain, + description: 'Domain to confirm release' + string :action + string :initiator, + default: nil + + validates :domain, :action, presence: true + validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED, + RegistrantVerification::REJECTED] } + + def raise_errors!(domain) + return unless domain.errors.any? + + message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" + throw message + end + end + end +end diff --git a/app/interactions/domains/delete_confirm/process_action.rb b/app/interactions/domains/delete_confirm/process_action.rb new file mode 100644 index 000000000..59f23de67 --- /dev/null +++ b/app/interactions/domains/delete_confirm/process_action.rb @@ -0,0 +1,17 @@ +module Domains + module DeleteConfirm + class ProcessAction < Base + def execute + ::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\ + " #{initiator}" + + case action + when RegistrantVerification::CONFIRMED + compose(ProcessDeleteConfirmed, inputs) + when RegistrantVerification::REJECTED + compose(ProcessDeleteRejected, inputs) + end + end + end + end +end diff --git a/app/interactions/domains/delete_confirm/process_delete_confirmed.rb b/app/interactions/domains/delete_confirm/process_delete_confirmed.rb new file mode 100644 index 000000000..553809666 --- /dev/null +++ b/app/interactions/domains/delete_confirm/process_delete_confirmed.rb @@ -0,0 +1,11 @@ +module Domains + module DeleteConfirm + class ProcessDeleteConfirmed < Base + def execute + domain.notify_registrar(:poll_pending_delete_confirmed_by_registrant) + domain.apply_pending_delete! + raise_errors!(domain) + end + end + end +end diff --git a/app/interactions/domains/delete_confirm/process_delete_rejected.rb b/app/interactions/domains/delete_confirm/process_delete_rejected.rb new file mode 100644 index 000000000..5038ba150 --- /dev/null +++ b/app/interactions/domains/delete_confirm/process_delete_rejected.rb @@ -0,0 +1,34 @@ +module Domains + module DeleteConfirm + class ProcessDeleteRejected < Base + def execute + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) + domain.notify_registrar(:poll_pending_delete_rejected_by_registrant) + + domain.cancel_pending_delete + domain.save(validate: false) + raise_errors!(domain) + + send_domain_deleted_email + end + + def send_domain_deleted_email + if domain.registrant_verification_token.blank? + warn "EMAIL NOT DELIVERED: registrant_verification_token is missing for #{domain.name}" + elsif domain.registrant_verification_asked_at.blank? + warn "EMAIL NOT DELIVERED: registrant_verification_asked_at is missing for #{domain.name}" + else + send_email + end + end + + def warn(message) + Rails.logger.warn(message) + end + + def send_email + DomainDeleteMailer.rejected(domain).deliver_now + end + end + end +end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/jobs/domain_delete_confirm_job.rb b/app/jobs/domain_delete_confirm_job.rb index e94f2432c..afea3da59 100644 --- a/app/jobs/domain_delete_confirm_job.rb +++ b/app/jobs/domain_delete_confirm_job.rb @@ -1,39 +1,11 @@ -class DomainDeleteConfirmJob < Que::Job - def run(domain_id, action, initiator = nil) - ::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{action} by #{initiator}" - # it's recommended to keep transaction against job table as short as possible. - ActiveRecord::Base.transaction do - domain = Epp::Domain.find(domain_id) +class DomainDeleteConfirmJob < ApplicationJob + queue_as :default - case action - when RegistrantVerification::CONFIRMED - domain.notify_registrar(:poll_pending_delete_confirmed_by_registrant) - domain.apply_pending_delete! - raise_errors!(domain) + def perform(domain_id, action, initiator = nil) + domain = Epp::Domain.find(domain_id) - when RegistrantVerification::REJECTED - domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) - domain.notify_registrar(:poll_pending_delete_rejected_by_registrant) - - domain.cancel_pending_delete - domain.save(validate: false) - raise_errors!(domain) - - if domain.registrant_verification_token.blank? - Rails.logger.warn "EMAIL NOT DELIVERED: registrant_verification_token is missing for #{domain.name}" - elsif domain.registrant_verification_asked_at.blank? - Rails.logger.warn "EMAIL NOT DELIVERED: registrant_verification_asked_at is missing for #{domain.name}" - else - DomainDeleteMailer.rejected(domain).deliver_now - end - end - - destroy # it's best to destroy the job in the same transaction - end - end - - - def raise_errors!(domain) - throw "domain #{domain.name} failed with errors #{domain.errors.full_messages}" if domain.errors.any? + Domains::DeleteConfirm::ProcessAction.run(domain: domain, + action: action, + initiator: initiator) end end diff --git a/app/models/registrant_verification.rb b/app/models/registrant_verification.rb index 097f0cfa9..f0c9f3b97 100644 --- a/app/models/registrant_verification.rb +++ b/app/models/registrant_verification.rb @@ -30,12 +30,12 @@ class RegistrantVerification < ApplicationRecord def domain_registrant_delete_confirm!(initiator) self.action_type = DOMAIN_DELETE self.action = CONFIRMED - DomainDeleteConfirmJob.enqueue domain.id, CONFIRMED, initiator if save + DomainDeleteConfirmJob.perform_later domain.id, CONFIRMED, initiator if save end def domain_registrant_delete_reject!(initiator) self.action_type = DOMAIN_DELETE self.action = REJECTED - DomainDeleteConfirmJob.enqueue domain.id, REJECTED, initiator if save + DomainDeleteConfirmJob.perform_later domain.id, REJECTED, initiator if save end end diff --git a/test/jobs/domain_delete_confirm_job_test.rb b/test/jobs/domain_delete_confirm_job_test.rb index b999bd3c7..cbe4b87f1 100644 --- a/test/jobs/domain_delete_confirm_job_test.rb +++ b/test/jobs/domain_delete_confirm_job_test.rb @@ -18,7 +18,7 @@ class DomainDeleteConfirmJobTest < ActiveSupport::TestCase new_registrant_email: @new_registrant.email, current_user_id: @user.id }) - DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED) + DomainDeleteConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) last_registrar_notification = @domain.registrar.notifications.last assert_equal(last_registrar_notification.attached_obj_id, @domain.id) @@ -31,7 +31,7 @@ class DomainDeleteConfirmJobTest < ActiveSupport::TestCase new_registrant_email: @new_registrant.email, current_user_id: @user.id }) - DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED) + DomainDeleteConfirmJob.perform_now(@domain.id, RegistrantVerification::CONFIRMED) last_registrar_notification = @domain.registrar.notifications.last assert_equal(last_registrar_notification.attached_obj_id, @domain.id) @@ -51,7 +51,7 @@ class DomainDeleteConfirmJobTest < ActiveSupport::TestCase assert @domain.registrant_delete_confirmable?(@domain.registrant_verification_token) assert_equal @user.id, @domain.pending_json['current_user_id'] - DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED) + DomainDeleteConfirmJob.perform_now(@domain.id, RegistrantVerification::CONFIRMED) @domain.reload assert @domain.statuses.include? DomainStatus::PENDING_DELETE @@ -72,7 +72,7 @@ class DomainDeleteConfirmJobTest < ActiveSupport::TestCase assert @domain.registrant_delete_confirmable?(@domain.registrant_verification_token) assert_equal @user.id, @domain.pending_json['current_user_id'] - DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED) + DomainDeleteConfirmJob.perform_now(@domain.id, RegistrantVerification::REJECTED) @domain.reload assert_equal ['ok'], @domain.statuses diff --git a/test/system/registrant_area/domains/domain_delete_confirms_test.rb b/test/system/registrant_area/domains/domain_delete_confirms_test.rb index 0eb61ada8..765cd0149 100644 --- a/test/system/registrant_area/domains/domain_delete_confirms_test.rb +++ b/test/system/registrant_area/domains/domain_delete_confirms_test.rb @@ -1,6 +1,7 @@ require 'application_system_test_case' class DomainDeleteConfirmsTest < ApplicationSystemTestCase + include ActionMailer::TestHelper setup do @user = users(:registrant) sign_in @user @@ -13,7 +14,9 @@ class DomainDeleteConfirmsTest < ApplicationSystemTestCase def test_enqueues_approve_job_after_verification visit registrant_domain_delete_confirm_url(@domain.id, token: @domain.registrant_verification_token) - click_on 'Confirm domain delete' + perform_enqueued_jobs do + click_on 'Confirm domain delete' + end assert_text 'Domain registrant change has successfully received.' @domain.reload @@ -23,7 +26,9 @@ class DomainDeleteConfirmsTest < ApplicationSystemTestCase def test_enqueues_reject_job_after_verification visit registrant_domain_delete_confirm_url(@domain.id, token: @domain.registrant_verification_token) - click_on 'Reject domain delete' + perform_enqueued_jobs do + click_on 'Reject domain delete' + end assert_text 'Domain registrant change has been rejected successfully.' @domain.reload From 5a059e86bb9bf380e610350adf705062276d726a Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 7 Dec 2020 17:08:54 +0500 Subject: [PATCH 167/202] Move confirmed delete procedures to interactor --- .../domains/delete_confirm/base.rb | 30 ++++++++++++++ .../process_delete_confirmed.rb | 41 ++++++++++++++++++- .../delete_confirm/process_delete_rejected.rb | 4 +- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/app/interactions/domains/delete_confirm/base.rb b/app/interactions/domains/delete_confirm/base.rb index d1d45f006..5bd07f40f 100644 --- a/app/interactions/domains/delete_confirm/base.rb +++ b/app/interactions/domains/delete_confirm/base.rb @@ -18,6 +18,36 @@ module Domains message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}" throw message end + + def notify_registrar(message_key) + domain.registrar.notifications.create!( + text: "#{I18n.t(message_key)}: #{domain.name}", + attached_obj_id: domain.id, + attached_obj_type: domain.class.to_s + ) + end + + def preclean_pendings + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + end + + def clean_pendings! + domain.is_admin = true + domain.registrant_verification_token = nil + domain.registrant_verification_asked_at = nil + domain.pending_json = {} + clear_statuses + domain.save + end + + def clear_statuses + domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) + domain.statuses.delete(DomainStatus::PENDING_UPDATE) + domain.statuses.delete(DomainStatus::PENDING_DELETE) + domain.status_notes[DomainStatus::PENDING_UPDATE] = '' + domain.status_notes[DomainStatus::PENDING_DELETE] = '' + end end end end diff --git a/app/interactions/domains/delete_confirm/process_delete_confirmed.rb b/app/interactions/domains/delete_confirm/process_delete_confirmed.rb index 553809666..1f3068107 100644 --- a/app/interactions/domains/delete_confirm/process_delete_confirmed.rb +++ b/app/interactions/domains/delete_confirm/process_delete_confirmed.rb @@ -2,10 +2,49 @@ module Domains module DeleteConfirm class ProcessDeleteConfirmed < Base def execute - domain.notify_registrar(:poll_pending_delete_confirmed_by_registrant) + notify_registrar(:poll_pending_delete_confirmed_by_registrant) domain.apply_pending_delete! raise_errors!(domain) end + + def apply_pending_delete! + preclean_pendings + clean_pendings! + DomainDeleteMailer.accepted(domain).deliver_now + domain.set_pending_delete! + end + + def set_pending_delete! + unless domain.pending_deletable? + add_epp_error + return + end + + domain.delete_date = delete_date + domain.statuses << DomainStatus::PENDING_DELETE + set_server_hold if server_holdable? + domain.save(validate: false) + end + + def set_server_hold + domain.statuses << DomainStatus::SERVER_HOLD + domain.outzone_at = Time.current + end + + def server_holdable? + return false if domain.statuses.include?(DomainStatus::SERVER_HOLD) + return false if domain.statuses.include?(DomainStatus::SERVER_MANUAL_INZONE) + + true + end + + def delete_date + Time.zone.today + Setting.redemption_grace_period.days + 1.day + end + + def add_epp_error + domain.add_epp_error('2304', nil, nil, I18n.t(:object_status_prohibits_operation)) + end end end end diff --git a/app/interactions/domains/delete_confirm/process_delete_rejected.rb b/app/interactions/domains/delete_confirm/process_delete_rejected.rb index 5038ba150..1844801ac 100644 --- a/app/interactions/domains/delete_confirm/process_delete_rejected.rb +++ b/app/interactions/domains/delete_confirm/process_delete_rejected.rb @@ -9,10 +9,10 @@ module Domains domain.save(validate: false) raise_errors!(domain) - send_domain_deleted_email + send_domain_delete_rejected_email end - def send_domain_deleted_email + def send_domain_delete_rejected_email if domain.registrant_verification_token.blank? warn "EMAIL NOT DELIVERED: registrant_verification_token is missing for #{domain.name}" elsif domain.registrant_verification_asked_at.blank? From ce07c5eae83c27f0043d98ad58a99bdb9bbc5dd4 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Mon, 7 Dec 2020 17:37:44 +0500 Subject: [PATCH 168/202] Move rejected delete procedures to interactor --- .../domains/delete_confirm/process_delete_rejected.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/interactions/domains/delete_confirm/process_delete_rejected.rb b/app/interactions/domains/delete_confirm/process_delete_rejected.rb index 1844801ac..25b491660 100644 --- a/app/interactions/domains/delete_confirm/process_delete_rejected.rb +++ b/app/interactions/domains/delete_confirm/process_delete_rejected.rb @@ -2,10 +2,8 @@ module Domains module DeleteConfirm class ProcessDeleteRejected < Base def execute - domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) - domain.notify_registrar(:poll_pending_delete_rejected_by_registrant) - domain.cancel_pending_delete + notify_registrar(:poll_pending_delete_rejected_by_registrant) domain.save(validate: false) raise_errors!(domain) From 6841703349339614256601367f7d352a286d8a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 8 Dec 2020 10:35:25 +0200 Subject: [PATCH 169/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9774d5cb3..6dd2e66dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +08.12.2020 +* Replaced Travis-CI with GitHub Actions [#1746](https://github.com/internetee/registry/pull/1746) + 01.12.2020 * Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) * Fixed internal error on removing clientHold status when not present [#1766](https://github.com/internetee/registry/issues/1766) From a54249f6ff7ecaddab9d9b6c7e7e174c59db5f25 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 8 Dec 2020 16:08:33 +0500 Subject: [PATCH 170/202] Move WhoisUpdate job to interactor --- app/interactions/whois/delete_record.rb | 48 ++++++++++++++ app/interactions/whois/update.rb | 39 ++++++++++++ app/interactions/whois/update_record.rb | 32 ++++++++++ app/jobs/update_whois_record_job.rb | 84 +------------------------ 4 files changed, 120 insertions(+), 83 deletions(-) create mode 100644 app/interactions/whois/delete_record.rb create mode 100644 app/interactions/whois/update.rb create mode 100644 app/interactions/whois/update_record.rb diff --git a/app/interactions/whois/delete_record.rb b/app/interactions/whois/delete_record.rb new file mode 100644 index 000000000..62e416d2f --- /dev/null +++ b/app/interactions/whois/delete_record.rb @@ -0,0 +1,48 @@ +module Whois + class DeleteRecord < ActiveInteraction::Base + string :name + string :type + + validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] } + + def execute + send "delete_#{type}", name + end + + # 1. deleting own + # 2. trying to regenerate reserved in order domain is still in the list + def delete_domain(name) + WhoisRecord.where(name: name).destroy_all + + BlockedDomain.find_by(name: name).try(:generate_data) + ReservedDomain.find_by(name: name).try(:generate_data) + Dispute.active.find_by(domain_name: name).try(:generate_data) + end + + def delete_reserved(name) + remove_status_from_whois(domain_name: name, domain_status: 'Reserved') + end + + def delete_blocked(name) + delete_reserved(name) + end + + def delete_disputed(name) + return if Dispute.active.find_by(domain_name: name).present? + + remove_status_from_whois(domain_name: name, domain_status: 'disputed') + end + + def delete_zone(name) + WhoisRecord.where(name: name).destroy_all + Whois::Record.where(name: name).destroy_all + end + + def remove_status_from_whois(domain_name:, domain_status:) + Whois::Record.where(name: domain_name).each do |r| + r.json['status'] = r.json['status'].delete_if { |status| status == domain_status } + r.json['status'].blank? ? r.destroy : r.save + end + end + end +end diff --git a/app/interactions/whois/update.rb b/app/interactions/whois/update.rb new file mode 100644 index 000000000..bd37b4576 --- /dev/null +++ b/app/interactions/whois/update.rb @@ -0,0 +1,39 @@ +module Whois + class Update < ActiveInteraction::Base + array :names + string :type + + validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] } + + def execute + ::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{type}" + + klass = determine_class + + Array(names).each do |name| + record = find_record(klass, name) + if record + Whois::UpdateRecord.run(record: record, type: type) + else + Whois::DeleteRecord.run(name: name, type: type) + end + end + end + + private + + def determine_class + case type + when 'reserved' then ReservedDomain + when 'blocked' then BlockedDomain + when 'domain' then Domain + when 'disputed' then Dispute.active + else DNS::Zone + end + end + + def find_record(klass, name) + klass == DNS::Zone ? klass.find_by(origin: name) : klass.find_by(name: name) + end + end +end diff --git a/app/interactions/whois/update_record.rb b/app/interactions/whois/update_record.rb new file mode 100644 index 000000000..6fc838968 --- /dev/null +++ b/app/interactions/whois/update_record.rb @@ -0,0 +1,32 @@ +module Whois + class UpdateRecord < ActiveInteraction::Base + interface :record + string :type + + validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] } + + def execute + send "update_#{type}", record + end + + def update_domain(domain) + domain.whois_record ? domain.whois_record.save : domain.create_whois_record + end + + def update_reserved(record) + record.generate_data + end + + def update_blocked(record) + update_reserved(record) + end + + def update_disputed(record) + update_reserved(record) + end + + def update_zone(record) + update_reserved(record) + end + end +end diff --git a/app/jobs/update_whois_record_job.rb b/app/jobs/update_whois_record_job.rb index 2973d5a6b..d067807a0 100644 --- a/app/jobs/update_whois_record_job.rb +++ b/app/jobs/update_whois_record_job.rb @@ -1,87 +1,5 @@ class UpdateWhoisRecordJob < Que::Job - def run(names, type) - ::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{type}" - - klass = determine_class(type) - - Array(names).each do |name| - record = find_record(klass, name) - if record - send "update_#{type}", record - else - send "delete_#{type}", name - end - end - end - - def find_record(klass, name) - klass == DNS::Zone ? klass.find_by(origin: name) : klass.find_by(name: name) - end - - def determine_class(type) - case type - when 'reserved' then ReservedDomain - when 'blocked' then BlockedDomain - when 'domain' then Domain - when 'disputed' then Dispute.active - when 'zone' then DNS::Zone - end - end - - def update_domain(domain) - domain.whois_record ? domain.whois_record.save : domain.create_whois_record - end - - def update_reserved(record) - record.generate_data - end - - def update_blocked(record) - update_reserved(record) - end - - def update_disputed(record) - update_reserved(record) - end - - def update_zone(record) - update_reserved(record) - end - - # 1. deleting own - # 2. trying to regenerate reserved in order domain is still in the list - def delete_domain(name) - WhoisRecord.where(name: name).destroy_all - - BlockedDomain.find_by(name: name).try(:generate_data) - ReservedDomain.find_by(name: name).try(:generate_data) - Dispute.active.find_by(domain_name: name).try(:generate_data) - end - - def delete_reserved(name) - remove_status_from_whois(domain_name: name, domain_status: 'Reserved') - end - - def delete_blocked(name) - delete_reserved(name) - end - - def delete_disputed(name) - return if Dispute.active.find_by(domain_name: name).present? - - remove_status_from_whois(domain_name: name, domain_status: 'disputed') - end - - def delete_zone(name) - WhoisRecord.where(name: name).destroy_all - Whois::Record.where(name: name).destroy_all - end - - def remove_status_from_whois(domain_name:, domain_status:) - Whois::Record.where(name: domain_name).each do |r| - r.json['status'] = r.json['status'].delete_if { |status| status == domain_status } - r.json['status'].blank? ? r.destroy : r.save - end + Whois::Update.run(names: [names].flatten, type: type) end end From 8f6f2433e24cd0f3918c786223abd63d8e38b017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 8 Dec 2020 18:17:10 +0200 Subject: [PATCH 171/202] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dd2e66dd..5e823c7b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 08.12.2020 * Replaced Travis-CI with GitHub Actions [#1746](https://github.com/internetee/registry/pull/1746) +* Refactored domain delete for interactors [#1755](https://github.com/internetee/registry/issues/1755) 01.12.2020 * Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751) From 7573a7eac2e1dd7a5540855a491f367dec8113e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 9 Dec 2020 16:33:23 +0200 Subject: [PATCH 172/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e823c7b1..7300adbdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +09.12.2020 +* Refactored domain update confirm for interactors [#1760](https://github.com/internetee/registry/issues/1760) + 08.12.2020 * Replaced Travis-CI with GitHub Actions [#1746](https://github.com/internetee/registry/pull/1746) * Refactored domain delete for interactors [#1755](https://github.com/internetee/registry/issues/1755) From de37732a56abf42c06b165e208c01c03d91a22e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Mon, 14 Dec 2020 15:21:04 +0200 Subject: [PATCH 173/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7300adbdb..60d7c1e24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +14.12.2020 +* Refactored domain cron jobs for interactors [#1767](https://github.com/internetee/registry/issues/1767) + 09.12.2020 * Refactored domain update confirm for interactors [#1760](https://github.com/internetee/registry/issues/1760) From ee2601b8de655359c7de3472cd8ce252ddcab119 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Wed, 9 Dec 2020 16:50:53 +0500 Subject: [PATCH 174/202] Add scaffold for a controller & view --- Gemfile | 1 + Gemfile.lock | 7 ++++++ .../registrar/bulk_renew_controller.rb | 9 ++++++++ .../bulk_change/_api_errors.html.erb | 9 ++++++++ .../bulk_change/_bulk_renew_form.html.erb | 22 +++++++++++++++++++ app/views/registrar/bulk_change/new.html.erb | 8 +++++++ config/locales/registrar/bulk_change.en.yml | 5 +++++ config/routes.rb | 2 ++ 8 files changed, 63 insertions(+) create mode 100644 app/controllers/registrar/bulk_renew_controller.rb create mode 100644 app/views/registrar/bulk_change/_api_errors.html.erb create mode 100644 app/views/registrar/bulk_change/_bulk_renew_form.html.erb diff --git a/Gemfile b/Gemfile index 9bbcba254..9aad8b7ae 100644 --- a/Gemfile +++ b/Gemfile @@ -80,6 +80,7 @@ gem 'directo', github: 'internetee/directo', branch: 'master' group :development, :test do + gem 'listen' gem 'pry', '0.10.1' gem 'puma' end diff --git a/Gemfile.lock b/Gemfile.lock index 2a0bb55b1..3c9da920e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,6 +250,9 @@ GEM kaminari-core (= 1.2.1) kaminari-core (1.2.1) libxml-ruby (3.2.0) + listen (3.3.3) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) logger (1.4.2) loofah (2.7.0) crass (~> 1.0.2) @@ -363,6 +366,9 @@ GEM activesupport (>= 5.2.1) i18n polyamorous (= 2.3.2) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) rbtree3 (0.6.0) regexp_parser (1.8.0) request_store (1.5.0) @@ -510,6 +516,7 @@ DEPENDENCIES jquery-ui-rails (= 5.0.5) kaminari lhv! + listen minitest (~> 5.14) money-rails nokogiri diff --git a/app/controllers/registrar/bulk_renew_controller.rb b/app/controllers/registrar/bulk_renew_controller.rb new file mode 100644 index 000000000..8818e416c --- /dev/null +++ b/app/controllers/registrar/bulk_renew_controller.rb @@ -0,0 +1,9 @@ +class Registrar + class BulkRenewController < DeppController + def index; end + + def new + authorize! :manage, :repp + end + end +end diff --git a/app/views/registrar/bulk_change/_api_errors.html.erb b/app/views/registrar/bulk_change/_api_errors.html.erb new file mode 100644 index 000000000..56bf8c404 --- /dev/null +++ b/app/views/registrar/bulk_change/_api_errors.html.erb @@ -0,0 +1,9 @@ +<% if @api_errors %> +
+
    + <% @api_errors.each do |error| %> +
  • <%= error[:title] %>
  • + <% end %> +
+
+<% end %> diff --git a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb new file mode 100644 index 000000000..38134e707 --- /dev/null +++ b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb @@ -0,0 +1,22 @@ +<%= form_tag registrar_new_registrar_bulk_renew_path, multipart: true, class: 'form-horizontal' do %> + <%= render 'api_errors' %> + + +
+
+ <%= label_tag 'expire_date', t('.expire_date') %> +
+
+ <%= text_field :expire_date, params[:expire_date], + class: 'form-control js-datepicker' %> +
+
+ +
+
+ +
+
+<% end %> diff --git a/app/views/registrar/bulk_change/new.html.erb b/app/views/registrar/bulk_change/new.html.erb index da85899ff..e61270b6d 100644 --- a/app/views/registrar/bulk_change/new.html.erb +++ b/app/views/registrar/bulk_change/new.html.erb @@ -19,6 +19,10 @@
  • <%= t '.bulk_transfer' %>
  • + +
  • + <%= t '.bulk_renew' %> +
  • @@ -34,4 +38,8 @@
    <%= render 'bulk_transfer_form' %>
    + +
    + <%= render 'bulk_renew_form' %> +
    diff --git a/config/locales/registrar/bulk_change.en.yml b/config/locales/registrar/bulk_change.en.yml index 7c1f11da9..d9f932a46 100644 --- a/config/locales/registrar/bulk_change.en.yml +++ b/config/locales/registrar/bulk_change.en.yml @@ -6,6 +6,7 @@ en: technical_contact: Technical contact nameserver: Nameserver bulk_transfer: Bulk transfer + bulk_renew: Bulk renew tech_contact_form: current_contact_id: Current contact ID @@ -29,3 +30,7 @@ en: submit_btn: Transfer help_btn: Toggle help help: Transfer domains in the csv file with correct transfer code to this registrar + + bulk_renew_form: + filter_btn: Filter + expire_date: Expire date diff --git a/config/routes.rb b/config/routes.rb index 0af3d95c9..2a6930d3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -133,6 +133,8 @@ Rails.application.routes.draw do end resources :domain_transfers, only: %i[new create] resource :bulk_change, controller: :bulk_change, only: :new + # resource :bulk_renew, controller: :bulk_renew #, only: :index + post '/registrar/bulk_renew/new', to: 'bulk_renew#new', as: :new_registrar_bulk_renew resource :tech_contacts, only: :update resource :nameservers, only: :update resources :contacts, constraints: {:id => /[^\/]+(?=#{ ActionController::Renderers::RENDERERS.map{|e| "\\.#{e}\\z"}.join("|") })|[^\/]+/} do From 9c662471a71671c76a1041a22fba12766f04fdea Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 10 Dec 2020 14:07:07 +0500 Subject: [PATCH 175/202] Fix controller & routes --- app/controllers/registrar/bulk_change_controller.rb | 7 +++++++ app/controllers/registrar/bulk_renew_controller.rb | 9 --------- .../registrar/bulk_change/_bulk_renew_form.html.erb | 8 ++++---- config/locales/registrar/bulk_change.en.yml | 2 +- config/routes.rb | 3 +-- 5 files changed, 13 insertions(+), 16 deletions(-) delete mode 100644 app/controllers/registrar/bulk_renew_controller.rb diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 441127f6c..1d9d606a4 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -4,9 +4,16 @@ 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 } end + def bulk_renew + authorize! :manage, :repp + @expire_date = params[:expire_date].to_date + render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew } + end + private def available_contacts diff --git a/app/controllers/registrar/bulk_renew_controller.rb b/app/controllers/registrar/bulk_renew_controller.rb deleted file mode 100644 index 8818e416c..000000000 --- a/app/controllers/registrar/bulk_renew_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Registrar - class BulkRenewController < DeppController - def index; end - - def new - authorize! :manage, :repp - end - end -end diff --git a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb index 38134e707..4b7cd392b 100644 --- a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb +++ b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb @@ -1,14 +1,14 @@ -<%= form_tag registrar_new_registrar_bulk_renew_path, multipart: true, class: 'form-horizontal' do %> +<%= form_with url: registrar_bulk_renew_path, multipart: true, class: 'form-horizontal' do |f|%> <%= render 'api_errors' %>
    - <%= label_tag 'expire_date', t('.expire_date') %> + <%= f.label :expire_date, t('.expire_date') %>
    - <%= text_field :expire_date, params[:expire_date], - class: 'form-control js-datepicker' %> + <%= f.text_field :expire_date, value: @expire_date, + class: 'form-control js-datepicker'%>
    diff --git a/config/locales/registrar/bulk_change.en.yml b/config/locales/registrar/bulk_change.en.yml index d9f932a46..51e6a8bb3 100644 --- a/config/locales/registrar/bulk_change.en.yml +++ b/config/locales/registrar/bulk_change.en.yml @@ -33,4 +33,4 @@ en: bulk_renew_form: filter_btn: Filter - expire_date: Expire date + expire_date: Expire date until diff --git a/config/routes.rb b/config/routes.rb index 2a6930d3f..7bf1ecd39 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -133,8 +133,7 @@ Rails.application.routes.draw do end resources :domain_transfers, only: %i[new create] resource :bulk_change, controller: :bulk_change, only: :new - # resource :bulk_renew, controller: :bulk_renew #, only: :index - post '/registrar/bulk_renew/new', to: 'bulk_renew#new', as: :new_registrar_bulk_renew + post '/bulk_renew/new', to: 'bulk_change#bulk_renew', as: :bulk_renew resource :tech_contacts, only: :update resource :nameservers, only: :update resources :contacts, constraints: {:id => /[^\/]+(?=#{ ActionController::Renderers::RENDERERS.map{|e| "\\.#{e}\\z"}.join("|") })|[^\/]+/} do From 40b22cd433b645c25567c27e440fce93226ba958 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Thu, 10 Dec 2020 14:35:04 +0500 Subject: [PATCH 176/202] Add multiple checkboxes to select domains for bulk renew --- .../registrar/bulk_change_controller.rb | 10 ++++++++++ .../bulk_change/_bulk_renew_form.html.erb | 17 +++++++++++++++++ config/locales/registrar/bulk_change.en.yml | 1 + 3 files changed, 28 insertions(+) diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 1d9d606a4..358a1949c 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -11,6 +11,8 @@ class Registrar def bulk_renew authorize! :manage, :repp @expire_date = params[:expire_date].to_date + # binding.pry + @domains = domains_by_date(@expire_date) render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew } end @@ -23,5 +25,13 @@ class Registrar def default_tab :technical_contact end + + def domains_scope + current_registrar_user.registrar.domains + end + + def domains_by_date(date) + domains_scope.where('valid_to <= ?', date) + end end end diff --git a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb index 4b7cd392b..874dc8eaa 100644 --- a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb +++ b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb @@ -12,6 +12,23 @@
    + <% if @domains.present? %> +
    +
    + <%= f.label :domain_ids, t('.domain_ids') %> +
    +
    + <%= f.collection_check_boxes :domain_ids, @domains, :id, :name, + checked: @domains.map(&:id) do |b|%> +
    + <%= b.check_box %> + <%= b.label %> +
    + <% end %> +
    +
    + <% end %> +
    -
    +
    + <%= f.submit "#{t '.filter_btn'}", name: 'filter', class: 'btn btn-warning' %> + <% if @domains.present? %> + <%= f.submit "#{t '.renew_btn'}", name: 'renew', class: 'btn btn-primary' %> + <% end %>
    <% end %> diff --git a/config/locales/registrar/bulk_change.en.yml b/config/locales/registrar/bulk_change.en.yml index 5043a3f94..75becfada 100644 --- a/config/locales/registrar/bulk_change.en.yml +++ b/config/locales/registrar/bulk_change.en.yml @@ -33,6 +33,7 @@ en: bulk_renew_form: filter_btn: Filter + renew_btn: Renew expire_date: Expire date until domain_ids: Domains for bulk renewal current_balance: Current balance diff --git a/test/system/registrar_area/bulk_change/bulk_renew_test.rb b/test/system/registrar_area/bulk_change/bulk_renew_test.rb index c99750e6c..bcb6c3d66 100644 --- a/test/system/registrar_area/bulk_change/bulk_renew_test.rb +++ b/test/system/registrar_area/bulk_change/bulk_renew_test.rb @@ -30,11 +30,9 @@ class BulkRenewTest < ApplicationSystemTestCase visit new_registrar_bulk_change_url click_link('Bulk renew') - select '1 year', from: 'Period' click_button 'Filter' - - click_button 'Filter' + click_button 'Renew' assert_text 'invalid.test' assert_no_text 'shop.test' From 95a789ab600def6a3bef10d77f6119643c0cf190 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 15 Dec 2020 13:54:47 +0500 Subject: [PATCH 188/202] Add better error handling for single domain renewal tasks --- .../domains/bulk_renew/single_domain_renew.rb | 13 +++++++--- app/interactions/domains/bulk_renew/start.rb | 25 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app/interactions/domains/bulk_renew/single_domain_renew.rb b/app/interactions/domains/bulk_renew/single_domain_renew.rb index fa6d3a7ae..66c6244b3 100644 --- a/app/interactions/domains/bulk_renew/single_domain_renew.rb +++ b/app/interactions/domains/bulk_renew/single_domain_renew.rb @@ -10,12 +10,11 @@ module Domains def execute in_transaction_with_retries do success = domain.renew(domain.valid_to, period, unit) - if success check_balance reduce_balance else - errors.add(:domain, I18n.t('domain_renew_error_for_domain', domain: domain.name)) + add_error end end end @@ -36,11 +35,11 @@ module Domains price: domain_pricelist) end - def in_transaction_with_retries(&block) + def in_transaction_with_retries if Rails.env.test? yield else - transaction_wrapper(block) + transaction_wrapper { yield } end rescue ActiveRecord::StatementInvalid sleep rand / 100 @@ -52,6 +51,12 @@ module Domains yield if block_given? end end + + private + + def add_error + errors.add(:domain, I18n.t('domain_renew_error_for_domain', domain: domain.name)) + end end end end diff --git a/app/interactions/domains/bulk_renew/start.rb b/app/interactions/domains/bulk_renew/start.rb index abd36bb76..d407afade 100644 --- a/app/interactions/domains/bulk_renew/start.rb +++ b/app/interactions/domains/bulk_renew/start.rb @@ -8,20 +8,22 @@ module Domains object :registrar def execute - if mass_check_balance.valid? && mass_check_balance.result + if renewable? domains.each do |domain| - Domains::BulkRenew::SingleDomainRenew.run(domain: domain, - period: period, - unit: unit, - registrar: registrar) + task = run_task(domain) + manage_errors(task) end else - errors.merge!(mass_check_balance.errors) + manage_errors(mass_check_balance) end end private + def renewable? + mass_check_balance.valid? && mass_check_balance.result + end + def period period_element.to_i.zero? ? 1 : period_element.to_i end @@ -37,6 +39,17 @@ module Domains unit: unit, balance: registrar.balance) end + + def manage_errors(task) + errors.merge!(task.errors) unless task.valid? + end + + def run_task(domain) + Domains::BulkRenew::SingleDomainRenew.run(domain: domain, + period: period, + unit: unit, + registrar: registrar) + end end end end From fafb02e9a04e463f5c1f39c2c215f55ac2838792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 15 Dec 2020 11:32:55 +0200 Subject: [PATCH 189/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d7c1e24..59ec26c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +15.12.2020 +* improved logic for domain list request in registrant API [#1750](https://github.com/internetee/registry/pull/1750) + 14.12.2020 * Refactored domain cron jobs for interactors [#1767](https://github.com/internetee/registry/issues/1767) From 13455b398cc9628c51dfd24013cd8f04daa8b5d8 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 15 Dec 2020 16:43:37 +0500 Subject: [PATCH 190/202] Add test & error message if not enough funds for renew --- app/interactions/domains/bulk_renew/start.rb | 1 + config/locales/en.yml | 1 + .../registrar_area/bulk_change/bulk_renew_test.rb | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/app/interactions/domains/bulk_renew/start.rb b/app/interactions/domains/bulk_renew/start.rb index d407afade..a0a2f9bf0 100644 --- a/app/interactions/domains/bulk_renew/start.rb +++ b/app/interactions/domains/bulk_renew/start.rb @@ -42,6 +42,7 @@ module Domains def manage_errors(task) errors.merge!(task.errors) unless task.valid? + errors.add(:domain, I18n.t('not_enough_funds')) unless task.result end def run_task(domain) diff --git a/config/locales/en.yml b/config/locales/en.yml index 3547b049f..22f29a6e3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -590,6 +590,7 @@ en: billing_failure_credit_balance_low: 'Billing failure - credit balance low' billing_failure_credit_balance_low_for_domain: 'Billing failure - credit balance low for %{domain}' domain_renew_error_for_domain: 'Domain renew error for %{domain}' + not_enough_funds: 'Not enough funds for renew domains' create: 'Create' activity_type: 'Activity type' receipt_date_from: 'Receipt date from' diff --git a/test/system/registrar_area/bulk_change/bulk_renew_test.rb b/test/system/registrar_area/bulk_change/bulk_renew_test.rb index bcb6c3d66..41c045903 100644 --- a/test/system/registrar_area/bulk_change/bulk_renew_test.rb +++ b/test/system/registrar_area/bulk_change/bulk_renew_test.rb @@ -37,4 +37,19 @@ class BulkRenewTest < ApplicationSystemTestCase assert_text 'invalid.test' assert_no_text 'shop.test' end + + def test_bulk_renew_checks_balance + sign_in users(:api_bestnames) + @price.update(price_cents: 99999999) + travel_to Time.zone.parse('2010-07-05 10:30') + + visit new_registrar_bulk_change_url + click_link('Bulk renew') + select '1 year', from: 'Period' + click_button 'Filter' + click_button 'Renew' + + assert_text 'Not enough funds for renew domains' + + end end From 06a59c9aebd9c8e303bfa2d6329db1711db4a6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Tue, 15 Dec 2020 18:57:43 +0200 Subject: [PATCH 191/202] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ec26c4c..d0cd1822c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 15.12.2020 -* improved logic for domain list request in registrant API [#1750](https://github.com/internetee/registry/pull/1750) +* Improved logic for domain list request in registrant API [#1750](https://github.com/internetee/registry/pull/1750) +* Refactored Whois update job for interactors [#1771](https://github.com/internetee/registry/issues/1771) 14.12.2020 * Refactored domain cron jobs for interactors [#1767](https://github.com/internetee/registry/issues/1767) From f1f8dba8254868d964de7bbb907854f2d5eed8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Wed, 16 Dec 2020 16:47:52 +0200 Subject: [PATCH 192/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0cd1822c..28d3fd67d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +16.12.2020 +* Refactored domain delete confirmation for interactors [#1769](https://github.com/internetee/registry/issues/1769) + 15.12.2020 * Improved logic for domain list request in registrant API [#1750](https://github.com/internetee/registry/pull/1750) * Refactored Whois update job for interactors [#1771](https://github.com/internetee/registry/issues/1771) From b833110ddd7294044692169718baafd381cec657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Thu, 17 Dec 2020 18:31:45 +0200 Subject: [PATCH 193/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28d3fd67d..0907d33e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +17.12.2020 +* New API for registering bounced emails [#1687](https://github.com/internetee/registry/pull/1687) + 16.12.2020 * Refactored domain delete confirmation for interactors [#1769](https://github.com/internetee/registry/issues/1769) From b8aeba6beb8d582bc04666412d21ae643476a14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Dec 2020 14:41:34 +0200 Subject: [PATCH 194/202] Move bulk domain renew BL to REPP --- .../registrar/bulk_change_controller.rb | 16 +++--- .../repp/v1/domains/renews_controller.rb | 51 +++++++++++++++++++ app/models/depp/domain.rb | 9 ++++ .../bulk_change/_bulk_renew_form.html.erb | 4 +- config/routes.rb | 1 + 5 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 app/controllers/repp/v1/domains/renews_controller.rb diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 1dc5282a7..1c3d85cc1 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -13,12 +13,14 @@ class Registrar set_form_data if ready_to_renew? - domains = Epp::Domain.where(id: domain_ids_for_bulk_renew).to_a - task = renew_task(domains) - flash[:notice] = flash_message(task) + res = Depp::Domain.bulk_renew(domain_ids_for_bulk_renew, params[:period], + current_registrar_user.registrar) + + flash_message(JSON.parse(res)) else flash[:notice] = nil end + render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew } end @@ -60,12 +62,8 @@ class Registrar registrar: current_registrar_user.registrar) end - def flash_message(task) - if task.valid? - t(:bulk_renew_completed) - else - task.errors.full_messages.join(' and ') - end + def flash_message(res) + flash[:notice] = res['code'] == 1000 ? t(:bulk_renew_completed) : res['message'] end end end diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb new file mode 100644 index 000000000..46dd9fbba --- /dev/null +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -0,0 +1,51 @@ +module Repp + module V1 + module Domains + class RenewsController < BaseController + def bulk_renew + @epp_errors ||= [] + + if bulk_renew_params[:domains].instance_of?(Array) + domains = bulk_renew_domains + else + @epp_errors << { code: 2005, msg: 'Domains attribute must be an array' } + end + + return handle_errors if @epp_errors.any? + + 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: 2304, msg: renew.errors.full_messages.join(',') } + handle_errors + end + + private + + def run_bulk_renew_task(domains, period) + ::Domains::BulkRenew::Start.run(domains: domains, period_element: period, + registrar: current_user.registrar) + end + + def bulk_renew_params + params do + params.require(%i[domains renew_period]) + params.permit(:domains, :renew_period) + end + end + + def bulk_renew_domains + @epp_errors ||= [] + domains = [] + bulk_renew_params[:domains].each do |idn| + domain = Epp::Domain.find_by_idn(idn) + domains << domain if domain + @epp_errors << { code: 2304, msg: "Object does not exist: #{idn}" } unless domain + end + + domains + end + end + end + end +end diff --git a/app/models/depp/domain.rb b/app/models/depp/domain.rb index 3bb3b7473..a2acde07a 100644 --- a/app/models/depp/domain.rb +++ b/app/models/depp/domain.rb @@ -30,6 +30,15 @@ module Depp ['10 years', '10y'], ] + def self.bulk_renew(domains, period, _registrar) + payload = { domains: domains, renew_period: period } + headers = { Authorization: 'Basic dGVzdDp0ZXN0MTIz' } + + RestClient.post("#{ENV['repp_url']}domains/renew/bulk", payload, headers).response + rescue RestClient::ExceptionWithResponse => e + e.response + end + def initialize(args = {}) self.current_user = args[:current_user] self.epp_xml = EppXml::Domain.new(cl_trid_prefix: current_user.tag) diff --git a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb index 239a36815..5db40365f 100644 --- a/app/views/registrar/bulk_change/_bulk_renew_form.html.erb +++ b/app/views/registrar/bulk_change/_bulk_renew_form.html.erb @@ -37,8 +37,8 @@ <%= f.label :domain_ids, t('.domain_ids') %>
    - <%= f.collection_check_boxes :domain_ids, @domains, :id, :name, - checked: @domains.map(&:id) do |b|%> + <%= f.collection_check_boxes :domain_ids, @domains, :name, :name, + checked: @domains.map(&:name) do |b|%>
    <%= b.check_box %> <%= b.label %> diff --git a/config/routes.rb b/config/routes.rb index 7bf1ecd39..2a2de1863 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -64,6 +64,7 @@ Rails.application.routes.draw do get ':id/transfer_info', to: 'domains#transfer_info', constraints: { id: /.*/ } post 'transfer', to: 'domains#transfer' patch 'contacts', to: 'domains/contacts#update' + post 'renew/bulk', to: 'domains/renews#bulk_renew' end end end From 240da481e567b61575b10800bda88067a437a64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Fri, 18 Dec 2020 15:53:04 +0200 Subject: [PATCH 195/202] Generate REPP auth token for bulk renew --- app/controllers/registrar/bulk_change_controller.rb | 2 +- app/models/depp/domain.rb | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 1c3d85cc1..1e8220aa5 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -14,7 +14,7 @@ class Registrar if ready_to_renew? res = Depp::Domain.bulk_renew(domain_ids_for_bulk_renew, params[:period], - current_registrar_user.registrar) + current_registrar_user) flash_message(JSON.parse(res)) else diff --git a/app/models/depp/domain.rb b/app/models/depp/domain.rb index a2acde07a..17fadab65 100644 --- a/app/models/depp/domain.rb +++ b/app/models/depp/domain.rb @@ -30,9 +30,10 @@ module Depp ['10 years', '10y'], ] - def self.bulk_renew(domains, period, _registrar) + def self.bulk_renew(domains, period, registrar) payload = { domains: domains, renew_period: period } - headers = { Authorization: 'Basic dGVzdDp0ZXN0MTIz' } + token = Base64.urlsafe_encode64("#{registrar.username}:#{registrar.plain_text_password}") + headers = { Authorization: "Basic #{token}" } RestClient.post("#{ENV['repp_url']}domains/renew/bulk", payload, headers).response rescue RestClient::ExceptionWithResponse => e From 66114aa648bb63d4296e01d5c06577300edbefba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 13:30:30 +0200 Subject: [PATCH 196/202] Create tests for REPP bulk renew, fix lingvistic issues with errors --- .../repp/v1/domains/renews_controller.rb | 34 +++++--- app/interactions/domains/bulk_renew/start.rb | 2 +- app/models/depp/domain.rb | 2 +- .../repp/v1/domains/bulk_renew_test.rb | 80 +++++++++++++++++++ 4 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 test/integration/repp/v1/domains/bulk_renew_test.rb diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index 46dd9fbba..052228e67 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -2,26 +2,40 @@ module Repp module V1 module Domains class RenewsController < BaseController + before_action :validate_renew_period, only: [:bulk_renew] + before_action :select_renewable_domains, only: [:bulk_renew] + def bulk_renew + 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: 2304, + msg: renew.errors.keys.map { |k, _v| renew.errors[k] }.join(',') } + handle_errors + end + + private + + def validate_renew_period + @epp_errors ||= [] + 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" } + end + + def select_renewable_domains @epp_errors ||= [] if bulk_renew_params[:domains].instance_of?(Array) - domains = bulk_renew_domains + @domains = bulk_renew_domains else @epp_errors << { code: 2005, msg: 'Domains attribute must be an array' } end return handle_errors if @epp_errors.any? - - 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: 2304, msg: renew.errors.full_messages.join(',') } - handle_errors end - private - def run_bulk_renew_task(domains, period) ::Domains::BulkRenew::Start.run(domains: domains, period_element: period, registrar: current_user.registrar) @@ -38,7 +52,7 @@ module Repp @epp_errors ||= [] domains = [] bulk_renew_params[:domains].each do |idn| - domain = Epp::Domain.find_by_idn(idn) + domain = Epp::Domain.find_by(name: idn) domains << domain if domain @epp_errors << { code: 2304, msg: "Object does not exist: #{idn}" } unless domain end diff --git a/app/interactions/domains/bulk_renew/start.rb b/app/interactions/domains/bulk_renew/start.rb index a0a2f9bf0..f758b52cd 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) - errors.merge!(task.errors) unless task.valid? + task.errors.each { |k, v| errors.add(k, v) } unless task.valid? errors.add(:domain, I18n.t('not_enough_funds')) unless task.result end diff --git a/app/models/depp/domain.rb b/app/models/depp/domain.rb index 17fadab65..e37f03334 100644 --- a/app/models/depp/domain.rb +++ b/app/models/depp/domain.rb @@ -35,7 +35,7 @@ module Depp token = Base64.urlsafe_encode64("#{registrar.username}:#{registrar.plain_text_password}") headers = { Authorization: "Basic #{token}" } - RestClient.post("#{ENV['repp_url']}domains/renew/bulk", payload, headers).response + RestClient.post("http://localhost:3000/repp/v1/domains/renew/bulk", payload, headers) rescue RestClient::ExceptionWithResponse => e e.response end diff --git a/test/integration/repp/v1/domains/bulk_renew_test.rb b/test/integration/repp/v1/domains/bulk_renew_test.rb new file mode 100644 index 000000000..fde383682 --- /dev/null +++ b/test/integration/repp/v1/domains/bulk_renew_test.rb @@ -0,0 +1,80 @@ +require 'test_helper' + +class ReppV1DomainsBulkRenewTest < ActionDispatch::IntegrationTest + def setup + travel_to Time.zone.parse('2010-07-05 10:30') + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + @domain = domains(:hospital) + + @auth_headers = { 'Authorization' => token } + end + + def test_renews_domains + payload = { + "domains": [ + 'shop.test', + 'airport.test', + 'library.test' + ], + "renew_period": "1y" + } + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert json[:data][:updated_domains].include? 'shop.test' + assert json[:data][:updated_domains].include? 'airport.test' + assert json[:data][:updated_domains].include? 'library.test' + end + + def test_throws_error_when_domain_not_renewable + payload = { + "domains": [ + 'invalid.test', + ], + "renew_period": "1y" + } + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'Domain renew error for invalid.test', json[:message] + end + + def test_throws_error_when_not_enough_balance + billing_prices(:renew_one_year).update(price_cents: 99999999) + payload = { + "domains": [ + 'invalid.test', + ], + "renew_period": "1y" + } + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2304, json[:code] + assert_equal 'Not enough funds for renew domains', json[:message] + end + + def test_throws_error_if_invalid_renew_period + payload = { + "domains": [ + 'shop.test' + ], + "renew_period": "nope" + } + + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2005, json[:code] + assert_equal 'Invalid renew period', json[:message] + end +end From 04944b8ac4680d493435254817a53a828ec316da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 14:19:27 +0200 Subject: [PATCH 197/202] Improve bulk renew tests --- .../registrar/bulk_change_controller.rb | 2 +- .../repp/v1/domains/renews_controller.rb | 6 +-- app/models/depp/domain.rb | 10 ---- app/models/repp_api.rb | 11 +++++ .../repp/v1/domains/bulk_renew_test.rb | 49 ++++++++++++------- .../bulk_change/bulk_renew_test.rb | 19 ++++++- 6 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 app/models/repp_api.rb diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index 1e8220aa5..f4f6707a5 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -13,7 +13,7 @@ class Registrar set_form_data if ready_to_renew? - res = Depp::Domain.bulk_renew(domain_ids_for_bulk_renew, params[:period], + res = ReppApi.bulk_renew(domain_ids_for_bulk_renew, params[:period], current_registrar_user) flash_message(JSON.parse(res)) diff --git a/app/controllers/repp/v1/domains/renews_controller.rb b/app/controllers/repp/v1/domains/renews_controller.rb index 052228e67..6b016dd86 100644 --- a/app/controllers/repp/v1/domains/renews_controller.rb +++ b/app/controllers/repp/v1/domains/renews_controller.rb @@ -9,8 +9,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: 2304, - msg: renew.errors.keys.map { |k, _v| renew.errors[k] }.join(',') } + @epp_errors << { code: 2002, + msg: renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ') } handle_errors end @@ -21,7 +21,7 @@ module Repp 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 << { code: 2005, msg: 'Invalid renew period' } end def select_renewable_domains diff --git a/app/models/depp/domain.rb b/app/models/depp/domain.rb index e37f03334..3bb3b7473 100644 --- a/app/models/depp/domain.rb +++ b/app/models/depp/domain.rb @@ -30,16 +30,6 @@ module Depp ['10 years', '10y'], ] - def self.bulk_renew(domains, period, registrar) - payload = { domains: domains, renew_period: period } - token = Base64.urlsafe_encode64("#{registrar.username}:#{registrar.plain_text_password}") - headers = { Authorization: "Basic #{token}" } - - RestClient.post("http://localhost:3000/repp/v1/domains/renew/bulk", payload, headers) - rescue RestClient::ExceptionWithResponse => e - e.response - end - def initialize(args = {}) self.current_user = args[:current_user] self.epp_xml = EppXml::Domain.new(cl_trid_prefix: current_user.tag) diff --git a/app/models/repp_api.rb b/app/models/repp_api.rb new file mode 100644 index 000000000..40c080a0b --- /dev/null +++ b/app/models/repp_api.rb @@ -0,0 +1,11 @@ +class ReppApi + def self.bulk_renew(domains, period, registrar) + payload = { domains: domains, renew_period: period } + token = Base64.urlsafe_encode64("#{registrar.username}:#{registrar.plain_text_password}") + headers = { Authorization: "Basic #{token}" } + + RestClient.post("#{ENV['repp_url']}domains/renew/bulk", payload, headers) + rescue RestClient::ExceptionWithResponse => e + e.response + end +end diff --git a/test/integration/repp/v1/domains/bulk_renew_test.rb b/test/integration/repp/v1/domains/bulk_renew_test.rb index fde383682..4cec91914 100644 --- a/test/integration/repp/v1/domains/bulk_renew_test.rb +++ b/test/integration/repp/v1/domains/bulk_renew_test.rb @@ -6,7 +6,6 @@ class ReppV1DomainsBulkRenewTest < ActionDispatch::IntegrationTest @user = users(:api_bestnames) token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") token = "Basic #{token}" - @domain = domains(:hospital) @auth_headers = { 'Authorization' => token } end @@ -20,15 +19,22 @@ class ReppV1DomainsBulkRenewTest < ActionDispatch::IntegrationTest ], "renew_period": "1y" } - post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload - json = JSON.parse(response.body, symbolize_names: true) - assert_response :ok - assert_equal 1000, json[:code] - assert_equal 'Command completed successfully', json[:message] - assert json[:data][:updated_domains].include? 'shop.test' - assert json[:data][:updated_domains].include? 'airport.test' - assert json[:data][:updated_domains].include? 'library.test' + assert_changes -> { Domain.find_by(name: 'shop.test').valid_to } do + assert_changes -> { Domain.find_by(name: 'airport.test').valid_to } do + assert_changes -> { Domain.find_by(name: 'library.test').valid_to } do + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert json[:data][:updated_domains].include? 'shop.test' + assert json[:data][:updated_domains].include? 'airport.test' + assert json[:data][:updated_domains].include? 'library.test' + end + end + end end def test_throws_error_when_domain_not_renewable @@ -38,12 +44,14 @@ class ReppV1DomainsBulkRenewTest < ActionDispatch::IntegrationTest ], "renew_period": "1y" } - post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload - json = JSON.parse(response.body, symbolize_names: true) + assert_no_changes -> { Domain.find_by(name: 'invalid.test').valid_to } do + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) - assert_response :bad_request - assert_equal 2304, json[:code] - assert_equal 'Domain renew error for invalid.test', json[:message] + assert_response :bad_request + assert_equal 2002, json[:code] + assert_equal 'Domain renew error for invalid.test', json[:message] + end end def test_throws_error_when_not_enough_balance @@ -54,12 +62,15 @@ class ReppV1DomainsBulkRenewTest < ActionDispatch::IntegrationTest ], "renew_period": "1y" } - post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload - json = JSON.parse(response.body, symbolize_names: true) - assert_response :bad_request - assert_equal 2304, json[:code] - assert_equal 'Not enough funds for renew domains', json[:message] + assert_no_changes -> { Domain.find_by(name: 'invalid.test').valid_to } do + post "/repp/v1/domains/renew/bulk", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 2002, json[:code] + assert_equal 'Not enough funds for renew domains', json[:message] + end end def test_throws_error_if_invalid_renew_period diff --git a/test/system/registrar_area/bulk_change/bulk_renew_test.rb b/test/system/registrar_area/bulk_change/bulk_renew_test.rb index 41c045903..5e1704afd 100644 --- a/test/system/registrar_area/bulk_change/bulk_renew_test.rb +++ b/test/system/registrar_area/bulk_change/bulk_renew_test.rb @@ -28,14 +28,21 @@ class BulkRenewTest < ApplicationSystemTestCase sign_in users(:api_bestnames) travel_to Time.zone.parse('2010-07-05 10:30') + req_body = { domains: ["shop.test", "airport.test", "library.test", "invalid.test"], renew_period: "1y" } + stub_request(:post, "#{ENV['repp_url']}domains/renew/bulk").with(body: req_body) + .to_return(status: 400, body: { + code: 2304, + message: "Domain renew error for invalid.test", + data: {} + }.to_json) + visit new_registrar_bulk_change_url click_link('Bulk renew') select '1 year', from: 'Period' click_button 'Filter' click_button 'Renew' - assert_text 'invalid.test' - assert_no_text 'shop.test' + assert_text 'Domain renew error for invalid.test' end def test_bulk_renew_checks_balance @@ -43,6 +50,14 @@ class BulkRenewTest < ApplicationSystemTestCase @price.update(price_cents: 99999999) travel_to Time.zone.parse('2010-07-05 10:30') + req_body = { domains: ["shop.test", "airport.test", "library.test", "invalid.test"], renew_period: "1y" } + stub_request(:post, "#{ENV['repp_url']}domains/renew/bulk").with(body: req_body) + .to_return(status: 400, body: { + code: 2304, + message: "Not enough funds for renew domains", + data: {} + }.to_json) + visit new_registrar_bulk_change_url click_link('Bulk renew') select '1 year', from: 'Period' From 01f6abb0d2d42b5fde8ec6e94afc0b5a0b152274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 14:41:19 +0200 Subject: [PATCH 198/202] Remove listen --- Gemfile | 1 - Gemfile.lock | 7 ------- 2 files changed, 8 deletions(-) diff --git a/Gemfile b/Gemfile index 9aad8b7ae..9bbcba254 100644 --- a/Gemfile +++ b/Gemfile @@ -80,7 +80,6 @@ gem 'directo', github: 'internetee/directo', branch: 'master' group :development, :test do - gem 'listen' gem 'pry', '0.10.1' gem 'puma' end diff --git a/Gemfile.lock b/Gemfile.lock index 3c9da920e..2a0bb55b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,9 +250,6 @@ GEM kaminari-core (= 1.2.1) kaminari-core (1.2.1) libxml-ruby (3.2.0) - listen (3.3.3) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) logger (1.4.2) loofah (2.7.0) crass (~> 1.0.2) @@ -366,9 +363,6 @@ GEM activesupport (>= 5.2.1) i18n polyamorous (= 2.3.2) - rb-fsevent (0.10.4) - rb-inotify (0.10.1) - ffi (~> 1.0) rbtree3 (0.6.0) regexp_parser (1.8.0) request_store (1.5.0) @@ -516,7 +510,6 @@ DEPENDENCIES jquery-ui-rails (= 5.0.5) kaminari lhv! - listen minitest (~> 5.14) money-rails nokogiri From e763411e5cfca571402c01213afe5ea055d5d09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 14:43:30 +0200 Subject: [PATCH 199/202] Fix CC issues --- app/controllers/registrar/bulk_change_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/registrar/bulk_change_controller.rb b/app/controllers/registrar/bulk_change_controller.rb index f4f6707a5..801ab0516 100644 --- a/app/controllers/registrar/bulk_change_controller.rb +++ b/app/controllers/registrar/bulk_change_controller.rb @@ -14,7 +14,7 @@ class Registrar if ready_to_renew? res = ReppApi.bulk_renew(domain_ids_for_bulk_renew, params[:period], - current_registrar_user) + current_registrar_user) flash_message(JSON.parse(res)) else From aac7a634874ecc05489e553a5e093805fc0cfe66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 16:49:39 +0200 Subject: [PATCH 200/202] Use plain Net::HTTP for bulk renew, include webclient certs --- app/models/repp_api.rb | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/app/models/repp_api.rb b/app/models/repp_api.rb index 40c080a0b..15ded62c5 100644 --- a/app/models/repp_api.rb +++ b/app/models/repp_api.rb @@ -1,11 +1,40 @@ class ReppApi def self.bulk_renew(domains, period, registrar) payload = { domains: domains, renew_period: period } - token = Base64.urlsafe_encode64("#{registrar.username}:#{registrar.plain_text_password}") - headers = { Authorization: "Basic #{token}" } + uri = URI.parse("#{ENV['repp_url']}domains/renew/bulk") + request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') + request.body = payload.to_json + request.basic_auth(registrar.username, registrar.plain_text_password) - RestClient.post("#{ENV['repp_url']}domains/renew/bulk", payload, headers) - rescue RestClient::ExceptionWithResponse => e - e.response + if Rails.env.test? + response = + Net::HTTP.start(uri.hostname, uri.port, + use_ssl: (uri.scheme == 'https'), + verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + http.request(request) + end + elsif Rails.env.development? + client_cert = File.read(ENV['cert_path']) + client_key = File.read(ENV['key_path']) + response = + Net::HTTP.start(uri.hostname, uri.port, + use_ssl: (uri.scheme == 'https'), + verify_mode: OpenSSL::SSL::VERIFY_NONE, + cert: OpenSSL::X509::Certificate.new(client_cert), + key: OpenSSL::PKey::RSA.new(client_key)) do |http| + http.request(request) + end + else + client_cert = File.read(ENV['cert_path']) + client_key = File.read(ENV['key_path']) + response = + Net::HTTP.start(uri.hostname, uri.port, + use_ssl: (uri.scheme == 'https'), + cert: OpenSSL::X509::Certificate.new(client_cert), + key: OpenSSL::PKey::RSA.new(client_key)) do |http| + http.request(request) + end + end + response.body end end From aff8e025bcfa3e1fd0735fb444a96839ed913d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Erik=20=C3=95unapuu?= Date: Mon, 21 Dec 2020 17:43:11 +0200 Subject: [PATCH 201/202] Move webclient cert appending to separate method --- app/models/repp_api.rb | 46 +++++++++++++----------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/app/models/repp_api.rb b/app/models/repp_api.rb index 15ded62c5..92e7a22c7 100644 --- a/app/models/repp_api.rb +++ b/app/models/repp_api.rb @@ -2,39 +2,21 @@ class ReppApi def self.bulk_renew(domains, period, registrar) payload = { domains: domains, renew_period: period } uri = URI.parse("#{ENV['repp_url']}domains/renew/bulk") - request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') - request.body = payload.to_json - request.basic_auth(registrar.username, registrar.plain_text_password) + req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') + req.body = payload.to_json - if Rails.env.test? - response = - Net::HTTP.start(uri.hostname, uri.port, - use_ssl: (uri.scheme == 'https'), - verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - http.request(request) - end - elsif Rails.env.development? - client_cert = File.read(ENV['cert_path']) - client_key = File.read(ENV['key_path']) - response = - Net::HTTP.start(uri.hostname, uri.port, - use_ssl: (uri.scheme == 'https'), - verify_mode: OpenSSL::SSL::VERIFY_NONE, - cert: OpenSSL::X509::Certificate.new(client_cert), - key: OpenSSL::PKey::RSA.new(client_key)) do |http| - http.request(request) - end - else - client_cert = File.read(ENV['cert_path']) - client_key = File.read(ENV['key_path']) - response = - Net::HTTP.start(uri.hostname, uri.port, - use_ssl: (uri.scheme == 'https'), - cert: OpenSSL::X509::Certificate.new(client_cert), - key: OpenSSL::PKey::RSA.new(client_key)) do |http| - http.request(request) - end + ReppApi.request(req, uri, registrar: registrar).body + end + + def self.request(request, uri, registrar:) + request.basic_auth(registrar.username, registrar.plain_text_password) if registrar + http = Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == 'https')) + unless Rails.env.test? + http.cert = OpenSSL::X509::Certificate.new(File.read(ENV['cert_path'])) + http.key = OpenSSL::PKey::RSA.new(File.read(ENV['key_path'])) end - response.body + http.verify_mode = OpenSSL::SSL::VERIFY_NONE if Rails.env.development? || Rails.env.test? + + http.request(request) end end From f80c597be89e044c4d12d26fd56d7e069d09bf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20V=C3=B5hmar?= Date: Mon, 21 Dec 2020 18:09:16 +0200 Subject: [PATCH 202/202] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0907d33e1..59e867d1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +21.12.2020 +* Bulk renew for REPP and registrar [#1763](https://github.com/internetee/registry/issues/1763) + 17.12.2020 * New API for registering bounced emails [#1687](https://github.com/internetee/registry/pull/1687)