diff --git a/app/interactions/actions/contact_create.rb b/app/interactions/actions/contact_create.rb index d31af2e38..84ed10caf 100644 --- a/app/interactions/actions/contact_create.rb +++ b/app/interactions/actions/contact_create.rb @@ -84,6 +84,7 @@ module Actions return false if @error contact.generate_code + contact.email_history = contact.email contact.save end end diff --git a/app/interactions/actions/contact_update.rb b/app/interactions/actions/contact_update.rb index d9a136dd3..685f1474c 100644 --- a/app/interactions/actions/contact_update.rb +++ b/app/interactions/actions/contact_update.rb @@ -112,6 +112,7 @@ module Actions email_changed = contact.will_save_change_to_email? old_email = contact.email_was + contact.email_history = old_email updated = contact.save if updated && email_changed && contact.registrant? diff --git a/app/interactions/domains/force_delete_email/base.rb b/app/interactions/domains/force_delete_email/base.rb index f22244045..8b3ec9965 100644 --- a/app/interactions/domains/force_delete_email/base.rb +++ b/app/interactions/domains/force_delete_email/base.rb @@ -23,11 +23,20 @@ module Domains def before_execute_force_delete(domain) if domain.force_delete_scheduled? && !domain.status_notes[DomainStatus::FORCE_DELETE].nil? added_additional_email_into_notes(domain) + notify_registrar(domain) else process_force_delete(domain) end end + def notify_registrar(domain) + domain.registrar.notifications.create!(text: I18n.t('force_delete_auto_email', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date, + email: domain.status_notes[DomainStatus::FORCE_DELETE])) + end + def process_force_delete(domain) domain.schedule_force_delete(type: :soft, notify_by_email: true, diff --git a/app/models/depp/contact.rb b/app/models/depp/contact.rb index 48e49d5e4..7007bcd00 100644 --- a/app/models/depp/contact.rb +++ b/app/models/depp/contact.rb @@ -5,7 +5,8 @@ module Depp attr_accessor :id, :name, :email, :phone, :org_name, :ident, :ident_type, :ident_country_code, :street, :city, :zip, :state, :country_code, - :password, :legal_document, :statuses, :code + :password, :legal_document, :statuses, :code, + :email_history DISABLED = 'Disabled' DISCLOSURE_TYPES = [DISABLED, '1', '0'] diff --git a/app/models/validation_event.rb b/app/models/validation_event.rb index 621fec030..49bf4325a 100644 --- a/app/models/validation_event.rb +++ b/app/models/validation_event.rb @@ -62,6 +62,7 @@ class ValidationEvent < ApplicationRecord if object.need_to_start_force_delete? start_force_delete elsif object.need_to_lift_force_delete? + refresh_status_notes lift_force_delete end end @@ -70,6 +71,33 @@ class ValidationEvent < ApplicationRecord Domains::ForceDeleteEmail::Base.run(email: email) end + def refresh_status_notes + domain_list.each do |domain| + next unless domain.status_notes[DomainStatus::FORCE_DELETE] + + domain.status_notes[DomainStatus::FORCE_DELETE].slice!(object.email_history) + domain.status_notes[DomainStatus::FORCE_DELETE].lstrip! + domain.save(validate: false) + + notify_registrar(domain) unless domain.status_notes[DomainStatus::FORCE_DELETE].empty? + end + end + + def domain_list + domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten + registrant_ids = Registrant.where(email: email).pluck(:id) + + (domain_contacts.map(&:domain).flatten + Domain.where(registrant_id: registrant_ids)).uniq + end + + def notify_registrar(domain) + domain.registrar.notifications.create!(text: I18n.t('force_delete_auto_email', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date, + email: domain.status_notes[DomainStatus::FORCE_DELETE])) + end + def lift_force_delete # domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten # registrant_ids = Registrant.where(email: email).pluck(:id) diff --git a/db/migrate/20220113201642_add_email_history_to_contacts.rb b/db/migrate/20220113201642_add_email_history_to_contacts.rb new file mode 100644 index 000000000..2a5f59953 --- /dev/null +++ b/db/migrate/20220113201642_add_email_history_to_contacts.rb @@ -0,0 +1,9 @@ +class AddEmailHistoryToContacts < ActiveRecord::Migration[6.1] + def change + add_column :contacts, :email_history, :string + + reversible do |dir| + dir.up { Contact.update_all('email_history = email') } + end + end +end diff --git a/db/migrate/20220113220809_add_email_history_to_registrars.rb b/db/migrate/20220113220809_add_email_history_to_registrars.rb new file mode 100644 index 000000000..1f89353a1 --- /dev/null +++ b/db/migrate/20220113220809_add_email_history_to_registrars.rb @@ -0,0 +1,9 @@ +class AddEmailHistoryToRegistrars < ActiveRecord::Migration[6.1] + def change + add_column :registrars, :email_history, :string + + reversible do |dir| + dir.up { Registrar.update_all('email_history = email') } + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 4403facfb..5b842d4a4 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -51,20 +51,6 @@ CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public; COMMENT ON EXTENSION hstore IS 'data type for storing sets of (key, value) pairs'; --- --- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public; - - --- --- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - --- - -COMMENT ON EXTENSION pg_stat_statements IS 'track execution statistics of all SQL statements executed'; - - -- -- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - -- @@ -216,8 +202,6 @@ CREATE FUNCTION public.generate_zonefile(i_origin character varying) RETURNS tex SET default_tablespace = ''; -SET default_with_oids = false; - -- -- Name: account_activities; Type: TABLE; Schema: public; Owner: - -- @@ -667,7 +651,8 @@ CREATE TABLE public.contacts ( upid integer, up_date timestamp without time zone, uuid uuid DEFAULT public.gen_random_uuid() NOT NULL, - disclosed_attributes character varying[] DEFAULT '{}'::character varying[] NOT NULL + disclosed_attributes character varying[] DEFAULT '{}'::character varying[] NOT NULL, + email_history character varying ); @@ -824,8 +809,7 @@ CREATE TABLE public.dnskeys ( creator_str character varying, updator_str character varying, legacy_domain_id integer, - updated_at timestamp without time zone, - validation_datetime timestamp without time zone + updated_at timestamp without time zone ); @@ -952,7 +936,6 @@ CREATE TABLE public.domains ( pending_json jsonb, force_delete_date date, statuses character varying[], - status_notes public.hstore, statuses_before_force_delete character varying[] DEFAULT '{}'::character varying[], upid integer, up_date timestamp without time zone, @@ -960,7 +943,8 @@ CREATE TABLE public.domains ( locked_by_registrant_at timestamp without time zone, force_delete_start timestamp without time zone, force_delete_data public.hstore, - json_statuses_history jsonb + json_statuses_history jsonb, + status_notes public.hstore ); @@ -2279,74 +2263,6 @@ CREATE SEQUENCE public.payment_orders_id_seq ALTER SEQUENCE public.payment_orders_id_seq OWNED BY public.payment_orders.id; --- --- Name: pghero_query_stats; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.pghero_query_stats ( - id bigint NOT NULL, - database text, - "user" text, - query text, - query_hash bigint, - total_time double precision, - calls bigint, - captured_at timestamp without time zone -); - - --- --- Name: pghero_query_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.pghero_query_stats_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: pghero_query_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.pghero_query_stats_id_seq OWNED BY public.pghero_query_stats.id; - - --- --- Name: pghero_space_stats; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.pghero_space_stats ( - id bigint NOT NULL, - database text, - schema text, - relation text, - size bigint, - captured_at timestamp without time zone -); - - --- --- Name: pghero_space_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.pghero_space_stats_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: pghero_space_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public.pghero_space_stats_id_seq OWNED BY public.pghero_space_stats.id; - - -- -- Name: prices; Type: TABLE; Schema: public; Owner: - -- @@ -2501,7 +2417,8 @@ CREATE TABLE public.registrars ( iban character varying, settings jsonb DEFAULT '{}'::jsonb NOT NULL, legaldoc_optout boolean DEFAULT false NOT NULL, - legaldoc_optout_comment text + legaldoc_optout_comment text, + email_history character varying ); @@ -2706,7 +2623,8 @@ CREATE TABLE public.validation_events ( validation_eventable_type character varying, validation_eventable_id bigint, 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, + event_type public.validation_type ); @@ -3248,20 +3166,6 @@ ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('publi ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass); --- --- Name: pghero_query_stats id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_query_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_query_stats_id_seq'::regclass); - - --- --- Name: pghero_space_stats id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_space_stats ALTER COLUMN id SET DEFAULT nextval('public.pghero_space_stats_id_seq'::regclass); - - -- -- Name: prices id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3785,22 +3689,6 @@ ALTER TABLE ONLY public.payment_orders ADD CONSTRAINT payment_orders_pkey PRIMARY KEY (id); --- --- Name: pghero_query_stats pghero_query_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_query_stats - ADD CONSTRAINT pghero_query_stats_pkey PRIMARY KEY (id); - - --- --- Name: pghero_space_stats pghero_space_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.pghero_space_stats - ADD CONSTRAINT pghero_space_stats_pkey PRIMARY KEY (id); - - -- -- Name: prices prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4571,20 +4459,6 @@ CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING b CREATE INDEX index_payment_orders_on_invoice_id ON public.payment_orders USING btree (invoice_id); --- --- Name: index_pghero_query_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_pghero_query_stats_on_database_and_captured_at ON public.pghero_query_stats USING btree (database, captured_at); - - --- --- Name: index_pghero_space_stats_on_database_and_captured_at; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_pghero_space_stats_on_database_and_captured_at ON public.pghero_space_stats USING btree (database, captured_at); - - -- -- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: - -- @@ -4641,6 +4515,13 @@ CREATE INDEX index_users_on_registrar_id ON public.users USING btree (registrar_ CREATE INDEX index_validation_events_on_event_data ON public.validation_events USING gin (event_data); +-- +-- Name: index_validation_events_on_event_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_validation_events_on_event_type ON public.validation_events USING btree (event_type); + + -- -- Name: index_validation_events_on_validation_eventable; Type: INDEX; Schema: public; Owner: - -- @@ -5386,16 +5267,15 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210708131814'), ('20210729131100'), ('20210729134625'), -('20211028122103'), -('20211028125245'), -('20211029082225'), +('20210827185249'), +('20211029073644'), ('20211124071418'), -('20211124084308'), ('20211125181033'), ('20211125184334'), ('20211126085139'), -('20211231113934'), -('20220106123143'); +('20220106123143'), +('20220113201642'), +('20220113220809'); diff --git a/test/fixtures/contacts.yml b/test/fixtures/contacts.yml index 4d45738bd..6e362d7a6 100644 --- a/test/fixtures/contacts.yml +++ b/test/fixtures/contacts.yml @@ -1,6 +1,7 @@ john: name: John email: john@inbox.test + email_history: john@inbox.test phone: '+555.555' ident: 1234 ident_type: priv @@ -18,6 +19,7 @@ john: william: &william name: William email: william@inbox.test + email_history: william@inbox.test phone: '+555.555' fax: '+666.6' ident: 12345 diff --git a/test/models/domain/force_delete_test.rb b/test/models/domain/force_delete_test.rb index f3294f034..974c445e6 100644 --- a/test/models/domain/force_delete_test.rb +++ b/test/models/domain/force_delete_test.rb @@ -411,6 +411,62 @@ class ForceDeleteTest < ActionMailer::TestCase assert notification.text.include? asserted_text end + def test_add_invalid_email_to_domain_status_notes + domain = domains(:airport) + domain.update(valid_to: Time.zone.parse('2012-08-05'), + statuses: %w[serverForceDelete serverRenewProhibited serverTransferProhibited], + force_delete_data: { 'template_name': 'invalid_email', 'force_delete_type': 'soft' }, + status_notes: { "serverForceDelete": '`@internet2.ee' }) + + travel_to Time.zone.parse('2010-07-05') + email = '`@internet.ee' + invalid_emails = '`@internet2.ee `@internet.ee' + asserted_text = "Invalid email: #{invalid_emails}" + + Truemail.configure.default_validation_type = :regex + + contact_first = domain.admin_contacts.first + contact_first.update_attribute(:email_history, 'john@inbox.test') + contact_first.update_attribute(:email, email) + + ValidationEvent::VALID_EVENTS_COUNT_THRESHOLD.times do + contact_first.verify_email + end + + domain.reload + + assert_equal domain.status_notes[DomainStatus::FORCE_DELETE], invalid_emails + notification = domain.registrar.notifications.last + assert notification.text.include? asserted_text + end + + def test_remove_invalid_email_from_domain_status_notes + domain = domains(:airport) + domain.update(valid_to: Time.zone.parse('2012-08-05'), + statuses: %w[serverForceDelete serverRenewProhibited serverTransferProhibited], + force_delete_data: { 'template_name': 'invalid_email', 'force_delete_type': 'soft' }, + status_notes: { "serverForceDelete": '`@internet2.ee `@internet.ee' }) + + travel_to Time.zone.parse('2010-07-05') + email = '`@internet2.ee' + invalid_email = '`@internet.ee' + asserted_text = "Invalid email: #{invalid_email}" + + Truemail.configure.default_validation_type = :regex + + contact_first = domain.admin_contacts.first + contact_first.update_attribute(:email_history, email) + contact_first.update_attribute(:email, 'john@inbox.test') + + travel_to Time.zone.parse('2010-07-05 0:00:03') + contact_first.verify_email + domain.reload + + assert_equal domain.status_notes[DomainStatus::FORCE_DELETE], invalid_email + notification = domain.registrar.notifications.last + assert notification.text.include? asserted_text + end + def test_domain_should_have_several_bounced_emails @domain.update(valid_to: Time.zone.parse('2012-08-05')) assert_not @domain.force_delete_scheduled? @@ -458,7 +514,7 @@ class ForceDeleteTest < ActionMailer::TestCase notification = @domain.registrar.notifications.last assert notification.text.include? asserted_text - @domain.registrant.update(email: 'aaa@bbb.com') + @domain.registrant.update(email: 'aaa@bbb.com', email_history: email) @domain.registrant.verify_email assert @domain.registrant.need_to_lift_force_delete? CheckForceDeleteLift.perform_now