diff --git a/.reek b/.reek index 54bff8852c2..0253240c30a 100644 --- a/.reek +++ b/.reek @@ -161,6 +161,7 @@ UtilityFunction: - LocaleHelper#locale_url_param - IdvSession#timed_out_vendor_error - JWT::Signature#sign + - SmsAccountResetCancellationNotifierJob#perform 'app/controllers': InstanceVariableAssumption: enabled: false diff --git a/app/controllers/account_reset/cancel_controller.rb b/app/controllers/account_reset/cancel_controller.rb index 7c12994e22e..8cbe8659e7f 100644 --- a/app/controllers/account_reset/cancel_controller.rb +++ b/app/controllers/account_reset/cancel_controller.rb @@ -1,8 +1,9 @@ module AccountReset class CancelController < ApplicationController def cancel - if AccountResetService.cancel_request(params[:token]) - handle_success + account_reset = AccountResetService.cancel_request(params[:token]) + if account_reset + handle_success(account_reset.user) else handle_failure end @@ -11,9 +12,13 @@ def cancel private - def handle_success - analytics.track_event(Analytics::ACCOUNT_RESET, event: :cancel, token_valid: true) + def handle_success(user) + analytics.track_event(Analytics::ACCOUNT_RESET, + event: :cancel, token_valid: true, user_id: user.uuid) sign_out if current_user + UserMailer.account_reset_cancel(user.email).deliver_later + phone = user.phone + SmsAccountResetCancellationNotifierJob.perform_now(phone: phone) if phone.present? flash[:success] = t('devise.two_factor_authentication.account_reset.successful_cancel') end diff --git a/app/controllers/account_reset/delete_account_controller.rb b/app/controllers/account_reset/delete_account_controller.rb index b97d9c5a2b3..d6a13530dc8 100644 --- a/app/controllers/account_reset/delete_account_controller.rb +++ b/app/controllers/account_reset/delete_account_controller.rb @@ -7,8 +7,10 @@ class DeleteAccountController < ApplicationController def show; end def delete - analytics.track_event(Analytics::ACCOUNT_RESET, event: :delete, token_valid: true) - email = reset_session_and_set_email + user = @account_reset_request.user + analytics.track_event(Analytics::ACCOUNT_RESET, + event: :delete, token_valid: true, user_id: user.uuid) + email = reset_session_and_set_email(user) UserMailer.account_reset_complete(email).deliver_later redirect_to account_reset_confirm_delete_account_url end @@ -19,8 +21,7 @@ def check_feature_enabled redirect_to root_url unless FeatureManagement.account_reset_enabled? end - def reset_session_and_set_email - user = @account_reset_request.user + def reset_session_and_set_email(user) email = user.email user.destroy! sign_out diff --git a/app/controllers/account_reset/request_controller.rb b/app/controllers/account_reset/request_controller.rb index 0b99bc2ace5..0eb7e428faa 100644 --- a/app/controllers/account_reset/request_controller.rb +++ b/app/controllers/account_reset/request_controller.rb @@ -28,10 +28,13 @@ def reset_session_with_email end def send_notifications - SmsAccountResetNotifierJob.perform_now( - phone: current_user.phone, - cancel_token: current_user.account_reset_request.request_token - ) + phone = current_user.phone + if phone + SmsAccountResetNotifierJob.perform_now( + phone: phone, + cancel_token: current_user.account_reset_request.request_token + ) + end UserMailer.account_reset_request(current_user).deliver_later end diff --git a/app/jobs/sms_account_reset_cancellation_notifier_job.rb b/app/jobs/sms_account_reset_cancellation_notifier_job.rb new file mode 100644 index 00000000000..d8f96b51e87 --- /dev/null +++ b/app/jobs/sms_account_reset_cancellation_notifier_job.rb @@ -0,0 +1,13 @@ +class SmsAccountResetCancellationNotifierJob < ApplicationJob + queue_as :sms + + def perform(phone:) + TwilioService::Utils.new.send_sms( + to: phone, + body: I18n.t( + 'jobs.sms_account_reset_cancel_job.message', + app: APP_NAME + ) + ) + end +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 72ff09e8954..2c6aa0e24e2 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -42,4 +42,8 @@ def account_reset_granted(user, account_reset) def account_reset_complete(email) mail(to: email, subject: t('user_mailer.account_reset_complete.subject')) end + + def account_reset_cancel(email) + mail(to: email, subject: t('user_mailer.account_reset_cancel.subject')) + end end diff --git a/app/services/account_reset_service.rb b/app/services/account_reset_service.rb index eefae3f4c12..901bdc5ee18 100644 --- a/app/services/account_reset_service.rb +++ b/app/services/account_reset_service.rb @@ -18,6 +18,7 @@ def self.cancel_request(token) account_reset.update(cancelled_at: Time.zone.now, request_token: nil, granted_token: nil) + account_reset end def self.report_fraud(token) diff --git a/app/views/user_mailer/account_reset_cancel.html.slim b/app/views/user_mailer/account_reset_cancel.html.slim new file mode 100644 index 00000000000..530ae2ec5f9 --- /dev/null +++ b/app/views/user_mailer/account_reset_cancel.html.slim @@ -0,0 +1,16 @@ +p.lead == t('.intro', app: link_to(APP_NAME, Figaro.env.mailer_domain_name, class: 'gray')) + +table.spacer + tbody + tr + td.s10 height="10px" + |   +table.hr + tr + th + |   + +p == t('.help', + app: link_to(APP_NAME, Figaro.env.mailer_domain_name, class: 'gray'), + help_link: link_to(t('user_mailer.help_link_text'), MarketingSite.help_url), + contact_link: link_to(t('user_mailer.contact_link_text'), MarketingSite.contact_url)) diff --git a/config/locales/devise/en.yml b/config/locales/devise/en.yml index e9602f0374c..1a0248de049 100644 --- a/config/locales/devise/en.yml +++ b/config/locales/devise/en.yml @@ -146,7 +146,7 @@ en: code below. please_try_again_html: Please try again in %{time_remaining}. account_reset: - successful_cancel: Thank you. The request to delete your login.gov account + successful_cancel: Thank you. Your request to delete your login.gov account has been cancelled. link: deleting your account text_html: If you can't use any of these security options above, you can reset diff --git a/config/locales/devise/es.yml b/config/locales/devise/es.yml index 1e273df55ee..72c5de6f910 100644 --- a/config/locales/devise/es.yml +++ b/config/locales/devise/es.yml @@ -151,8 +151,8 @@ es: el código de seguridad a continuación. please_try_again_html: Inténtelo de nuevo en %{time_remaining}. account_reset: - successful_cancel: Gracias. La solicitud para eliminar su cuenta de login.gov - ha sido cancelado. + successful_cancel: Gracias. Su solicitud para eliminar su cuenta de login.gov + ha sido cancelada. link: eliminando su cuenta text_html: Si no puede usar ninguna de estas opciones de seguridad anteriores, puede restablecer tus preferencias por %{link}. diff --git a/config/locales/devise/fr.yml b/config/locales/devise/fr.yml index f9f764116c5..a295a39d818 100644 --- a/config/locales/devise/fr.yml +++ b/config/locales/devise/fr.yml @@ -158,8 +158,8 @@ fr: le code de sécurité ci-dessous. please_try_again_html: Veuillez essayer de nouveau dans %{time_remaining}. account_reset: - successful_cancel: Je vous remercie. La demande de suppression de votre compte - login.gov a été annulé. + successful_cancel: Je vous remercie. Votre demande de suppression de votre + compte login.gov a été annulée. link: supprimer votre compte text_html: Si vous ne pouvez pas utiliser l'une de ces options de sécurité ci-dessus, vous pouvez réinitialiser vos préférences par %{link}. diff --git a/config/locales/jobs/en.yml b/config/locales/jobs/en.yml index d7d47937775..e2b078abf16 100644 --- a/config/locales/jobs/en.yml +++ b/config/locales/jobs/en.yml @@ -4,6 +4,8 @@ en: sms_otp_sender_job: message: "%{code} is your %{app} one-time security code. This code will expire in %{expiration} minutes." + sms_account_reset_cancel_job: + message: Your request to delete your login.gov account has been cancelled. sms_account_reset_notifier_job: message: 'You''ve requested to delete your login.gov account. Your request will be processed in 24 hours. If you don’t want to delete your account, please diff --git a/config/locales/jobs/es.yml b/config/locales/jobs/es.yml index e0f5c767b71..6ead589ceff 100644 --- a/config/locales/jobs/es.yml +++ b/config/locales/jobs/es.yml @@ -4,6 +4,8 @@ es: sms_otp_sender_job: message: "%{code} es su %{app} código de seguridad de sólo un uso. Este código caducará en %{expiration} minutos." + sms_account_reset_cancel_job: + message: Su solicitud para eliminar su cuenta de login.gov ha sido cancelada. sms_account_reset_notifier_job: message: 'Has solicitado eliminar tu cuenta de login.gov. Su solicitud será ser procesado en 24 horas. Si no desea eliminar su cuenta, por favor cancelar: diff --git a/config/locales/jobs/fr.yml b/config/locales/jobs/fr.yml index 76c1a828e42..f4509827b37 100644 --- a/config/locales/jobs/fr.yml +++ b/config/locales/jobs/fr.yml @@ -4,6 +4,8 @@ fr: sms_otp_sender_job: message: "%{code} est votre %{app} code de sécurité à utilisation unique. Ce code expirera dans %{expiration} minutes." + sms_account_reset_cancel_job: + message: Votre demande de suppression de votre compte login.gov a été annulée. sms_account_reset_notifier_job: message: 'Vous avez demandé à supprimer votre compte login.gov. Votre demande sera être traité en 24 heures. Si vous ne souhaitez pas supprimer votre compte, diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index 0ba57faca25..21e09a1bfdf 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -8,6 +8,11 @@ en: didn't request a password reset, you can ignore this message. link_text: Create your account subject: Your login.gov password reset request + account_reset_cancel: + intro: This email confirms you have cancelled the request to delete your login.gov + account. + subject: Delete your account + help: '' account_reset_request: help: '' intro: You’ve requested to delete your login.gov account.

Your diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index 0c4d9b723a7..a7349619979 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -5,6 +5,11 @@ es: intro: NOT TRANSLATED YET link_text: NOT TRANSLATED YET subject: NOT TRANSLATED YET + account_reset_cancel: + intro: Este correo electrónico confirma que ha cancelado la solicitud para eliminar + su cuenta de login.gov. + subject: Eliminar su cuenta + help: '' account_reset_request: help: '' intro: Has solicitado eliminar tu cuenta de login.gov.

Su la diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index 632f237141b..18556d9a1fd 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -5,6 +5,11 @@ fr: intro: NOT TRANSLATED YET link_text: NOT TRANSLATED YET subject: NOT TRANSLATED YET + account_reset_cancel: + intro: Cet e-mail confirme que vous avez annulé la demande de suppression de + votre compte login.gov. + subject: Supprimer votre compte + help: '' account_reset_request: help: '' intro: Vous avez demandé à supprimer votre compte login.gov.

Votre diff --git a/spec/controllers/account_reset/cancel_controller_spec.rb b/spec/controllers/account_reset/cancel_controller_spec.rb index d7f40f8377f..2cc508be842 100644 --- a/spec/controllers/account_reset/cancel_controller_spec.rb +++ b/spec/controllers/account_reset/cancel_controller_spec.rb @@ -1,14 +1,19 @@ require 'rails_helper' describe AccountReset::CancelController do + let(:user) { create(:user, :signed_up, phone: '+1 (703) 555-0000') } + before do + TwilioService::Utils.telephony_service = FakeSms + end + describe '#cancel' do it 'logs a good token to the analytics' do - user = create(:user) AccountResetService.new(user).create_request stub_analytics expect(@analytics).to receive(:track_event). - with(Analytics::ACCOUNT_RESET, event: :cancel, token_valid: true) + with(Analytics::ACCOUNT_RESET, + event: :cancel, token_valid: true, user_id: user.uuid) post :cancel, params: { token: AccountResetRequest.all[0].request_token } end @@ -25,5 +30,37 @@ post :cancel expect(response).to redirect_to root_url end + + it 'sends an SMS if there is a phone' do + AccountResetService.new(user).create_request + allow(SmsAccountResetCancellationNotifierJob).to receive(:perform_now) + + post :cancel, params: { token: AccountResetRequest.all[0].request_token } + + expect(SmsAccountResetCancellationNotifierJob).to have_received(:perform_now).with( + phone: user.phone + ) + end + + it 'does not send an SMS if there is no phone' do + AccountResetService.new(user).create_request + allow(SmsAccountResetCancellationNotifierJob).to receive(:perform_now) + user.phone = nil + user.save! + + post :cancel, params: { token: AccountResetRequest.all[0].request_token } + + expect(SmsAccountResetCancellationNotifierJob).to_not have_received(:perform_now) + end + + it 'sends an email' do + AccountResetService.new(user).create_request + + @mailer = instance_double(ActionMailer::MessageDelivery, deliver_later: true) + allow(UserMailer).to receive(:account_reset_cancel).with(user.email). + and_return(@mailer) + + post :cancel, params: { token: AccountResetRequest.all[0].request_token } + end end end diff --git a/spec/controllers/account_reset/delete_account_controller_spec.rb b/spec/controllers/account_reset/delete_account_controller_spec.rb index 909ecff1b84..c9c5030b982 100644 --- a/spec/controllers/account_reset/delete_account_controller_spec.rb +++ b/spec/controllers/account_reset/delete_account_controller_spec.rb @@ -10,7 +10,7 @@ session[:granted_token] = AccountResetRequest.all[0].granted_token stub_analytics expect(@analytics).to receive(:track_event). - with(Analytics::ACCOUNT_RESET, event: :delete, token_valid: true) + with(Analytics::ACCOUNT_RESET, event: :delete, token_valid: true, user_id: user.uuid) delete :delete end diff --git a/spec/jobs/sms_account_reset_cancellation_notifier_job_spec.rb b/spec/jobs/sms_account_reset_cancellation_notifier_job_spec.rb new file mode 100644 index 00000000000..2960013b35d --- /dev/null +++ b/spec/jobs/sms_account_reset_cancellation_notifier_job_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +describe SmsAccountResetCancellationNotifierJob do + include Features::ActiveJobHelper + + describe '.perform' do + before do + reset_job_queues + TwilioService::Utils.telephony_service = FakeSms + FakeSms.messages = [] + end + + subject(:perform) do + SmsAccountResetCancellationNotifierJob.perform_now( + phone: '+1 (888) 555-5555' + ) + end + + it 'sends a message to the mobile number', twilio: true do + allow(Figaro.env).to receive(:twilio_messaging_service_sid).and_return('fake_sid') + + TwilioService::Utils.telephony_service = FakeSms + + perform + + messages = FakeSms.messages + + expect(messages.size).to eq(1) + + msg = messages.first + + expect(msg.messaging_service_sid).to eq('fake_sid') + expect(msg.to).to eq('+1 (888) 555-5555') + expect(msg.body). + to eq(I18n.t('jobs.sms_account_reset_cancel_job.message', app: APP_NAME)) + end + end +end diff --git a/spec/services/account_reset_service_spec.rb b/spec/services/account_reset_service_spec.rb index 55670c9f9b2..6ad96b17490 100644 --- a/spec/services/account_reset_service_spec.rb +++ b/spec/services/account_reset_service_spec.rb @@ -37,12 +37,13 @@ describe '#cancel_request' do it 'removes tokens from a account reset request' do subject.create_request - AccountResetService.cancel_request(user.account_reset_request.request_token) + cancel = AccountResetService.cancel_request(user.account_reset_request.request_token) arr = AccountResetRequest.find_by(user_id: user.id) expect(arr.request_token).to_not be_present expect(arr.granted_token).to_not be_present expect(arr.requested_at).to be_present expect(arr.cancelled_at).to be_present + expect(arr).to eq(cancel) end it 'does not raise an error for a cancel request with a blank token' do