diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 1844917f7f1..d4c618b6b6c 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -376,6 +376,12 @@ def account_rejected end end + def suspended_reset_password + with_user_locale(user) do + mail(to: email_address.email, subject: t('user_mailer.suspended_reset_password.subject')) + end + end + private def email_should_receive_nonessential_notifications?(email) diff --git a/app/services/request_password_reset.rb b/app/services/request_password_reset.rb index aa2b588d369..ad2ca7ed5fd 100644 --- a/app/services/request_password_reset.rb +++ b/app/services/request_password_reset.rb @@ -26,6 +26,11 @@ def send_reset_password_instructions if throttle.throttled? analytics.throttler_rate_limit_triggered(throttle_type: :reset_password_email) irs_attempts_api_tracker.forgot_password_email_rate_limited(email: email) + elsif user.suspended? + UserMailer.with( + user: user, + email_address: email_address_record, + ).suspended_reset_password.deliver_now_or_later else token = user.set_reset_password_token UserMailer.with(user: user, email_address: email_address_record).reset_password_instructions( diff --git a/app/views/user_mailer/suspended_reset_password.html.erb b/app/views/user_mailer/suspended_reset_password.html.erb new file mode 100644 index 00000000000..aa33ddbe304 --- /dev/null +++ b/app/views/user_mailer/suspended_reset_password.html.erb @@ -0,0 +1,13 @@ + + + + + + +
+   +
+ +

+<%= t('user_mailer.suspended_reset_password.message', support_code: IdentityConfig.store.account_suspended_support_code, contact_number: IdentityConfig.store.idv_contact_phone_number) %> +

diff --git a/config/application.yml.default b/config/application.yml.default index 0ab6bf8714c..4a9b63a4dad 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -24,6 +24,7 @@ aamva_verification_url: https://example.org:12345/verification/url all_redirect_uris_cache_duration_minutes: 2 account_reset_token_valid_for_days: 1 account_reset_wait_period_days: 1 +account_suspended_support_code: EFGHI acuant_maintenance_window_start: acuant_maintenance_window_finish: acuant_assure_id_password: '' diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index 47455638ed7..36c6dbde09a 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -284,3 +284,8 @@ en: in with this email address, you can ignore this message. link_text: Go to %{app_name} reset_password_html: If you can’t remember your password, go to %{app_name} to reset it. + suspended_reset_password: + message: There was an issue resetting your password. Please give our contact + center a call at %{contact_number} and provide this code - + %{support_code}. + subject: We couldn’t reset your password diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index b6b8b12f536..dfe89bf2622 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -304,3 +304,8 @@ es: una sesión con este email, puede ignorar este mensaje. link_text: Ir a %{app_name} reset_password_html: Si no recuerda su contraseña, vaya a %{app_name} para restablecerla. + suspended_reset_password: + message: Se produjo un problema al restablecer su contraseña. Llame a nuestro + centro de atención al número %{contact_number} y proporcione este código + - %{support_code}. + subject: No pudimos restablecer su contraseña diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index 46e5293f5ee..c4575e15f2a 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -316,3 +316,8 @@ fr: link_text: Allez à %{app_name} reset_password_html: Si vous ne vous souvenez plus de votre mot de passe, allez à %{app_name} pour le réinitialiser. + suspended_reset_password: + message: La réinitialisation de votre mot de passe a posé un problème. Veuillez + appeler notre centre d’appels au %{contact_number} et fournir ce code - + %{support_code}. + subject: Nous n’avons pas pu réinitialiser votre mot de passe diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 394a5f89ad5..749d10aebb9 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -97,6 +97,7 @@ def self.build_store(config_map) config.add(:all_redirect_uris_cache_duration_minutes, type: :integer) config.add(:account_reset_token_valid_for_days, type: :integer) config.add(:account_reset_wait_period_days, type: :integer) + config.add(:account_suspended_support_code, type: :string) config.add(:acuant_maintenance_window_start, type: :timestamp, allow_nil: true) config.add(:acuant_maintenance_window_finish, type: :timestamp, allow_nil: true) config.add(:acuant_assure_id_password) diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 9460a4fff30..89b8e152aa0 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -32,6 +32,13 @@ def reset_password_instructions ) end + def suspended_reset_password + UserMailer.with( + user: user, + email_address: email_address_record, + ).suspended_reset_password + end + def password_changed UserMailer.with(user: user, email_address: email_address_record). password_changed(disavowal_token: SecureRandom.hex) diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 847e272b65a..13f95464e00 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -762,6 +762,35 @@ def expect_email_body_to_have_help_and_contact_links end end + describe '#suspended_reset_password' do + let(:mail) do + UserMailer.with(user: user, email_address: email_address). + suspended_reset_password + end + + it_behaves_like 'a system email' + it_behaves_like 'an email that respects user email locale preference' + + it 'sends to the specified email' do + expect(mail.to).to eq [email_address.email] + end + + it 'renders the subject' do + expect(mail.subject).to eq t('user_mailer.suspended_reset_password.subject') + end + + it 'renders the body' do + expect(mail.html_part.body). + to have_content( + t( + 'user_mailer.suspended_reset_password.message', + support_code: IdentityConfig.store.account_suspended_support_code, + contact_number: IdentityConfig.store.idv_contact_phone_number, + ), + ) + end + end + describe '#deliver_later' do it 'does not queue email if it potentially contains sensitive value' do user = create(:user) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index aa6de66bba3..3185e805989 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -774,7 +774,7 @@ end describe '#suspend!' do - context 'user is not already supsended' do + context 'user is not already suspended' do let(:mock_session_id) { SecureRandom.uuid } before do UpdateUser.new(user: user, attributes: { unique_session_id: mock_session_id }).call diff --git a/spec/services/request_password_reset_spec.rb b/spec/services/request_password_reset_spec.rb index 6534cb77f8b..912dc8005af 100644 --- a/spec/services/request_password_reset_spec.rb +++ b/spec/services/request_password_reset_spec.rb @@ -62,7 +62,7 @@ end end - it 'sends password reset instructions' do + it 'sets password reset token' do expect { subject }. to(change { user.reload.reset_password_token }) end @@ -81,6 +81,57 @@ end end + context 'when the user is found and confirmed, but is suspended' do + subject(:perform) do + described_class.new( + email: email, + irs_attempts_api_tracker: irs_attempts_api_tracker, + ).perform + end + + before do + user.suspend! + allow(UserMailer).to receive(:reset_password_instructions). + and_wrap_original do |impl, user, email, options| + token = options.fetch(:token) + expect(token).to be_present + expect(Devise.token_generator.digest(User, :reset_password_token, token)). + to eq(user.reset_password_token) + + impl.call(user, email, **options) + end + allow(UserMailer).to receive(:suspended_reset_password). + and_wrap_original do |impl, user, email, options| + token = options.fetch(:token) + expect(token).not_to be_present + + impl.call(user, email, **options) + end + end + + it 'does not set a password reset token' do + expect { subject }. + not_to(change { user.reload.reset_password_token }) + end + + it 'sends an email to the suspended user' do + expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1) + end + + it 'does not send a recovery activated push event' do + expect(PushNotification::HttpPush).not_to receive(:deliver). + with(PushNotification::RecoveryActivatedEvent.new(user: user)) + + subject + end + + it 'does not call irs tracking method forgot_password_email_sent' do + subject + + expect(irs_attempts_api_tracker).not_to have_received(:forgot_password_email_sent) + end + end + context 'when the user is found, not privileged, and not yet confirmed' do it 'sends password reset instructions' do allow(UserMailer).to receive(:reset_password_instructions).