From 434136bf3388e6a82636ba326f9abdcb65ce12a9 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 20 May 2020 14:24:32 -0400 Subject: [PATCH 01/27] WIP: account reset disavow **Why**: WIP --- .../account_reset/pending_controller.rb | 35 +++++++++++++++++++ .../pending_account_reset_request_concern.rb | 11 ++++++ app/controllers/users/sessions_controller.rb | 5 ++- .../account_reset/pending_presenter.rb | 22 ++++++++++++ app/views/account_reset/pending/show.html.erb | 9 +++++ config/routes.rb | 2 ++ ...ding_account_reset_request_concern_spec.rb | 24 +++++++++++++ .../users/sessions_controller_spec.rb | 15 ++++++++ 8 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 app/controllers/account_reset/pending_controller.rb create mode 100644 app/controllers/concerns/pending_account_reset_request_concern.rb create mode 100644 app/presenters/account_reset/pending_presenter.rb create mode 100644 app/views/account_reset/pending/show.html.erb create mode 100644 spec/controllers/concerns/pending_account_reset_request_concern_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb new file mode 100644 index 00000000000..9c3de3b5d93 --- /dev/null +++ b/app/controllers/account_reset/pending_controller.rb @@ -0,0 +1,35 @@ +module AccountReset + class PendingController < ApplicationController + include PendingAccountResetRequestConcern + + before_action :render_404_if_request_missing + + def show + analytics.track_event({ + event: 'account reset is pending', + user_id: current_user.uuid, + }) + @pending_presenter = AccountReset::PendingPresenter.new(account_reset_request) + end + + def cancel + rec_to_cancel = AccountResetRequest.find_by_id(params[:id]) + pp rec_to_cancel + rec_to_cancel.cancelled_at = Time.zone.now + rec_to_cancel.save! + # this is wrong; needs to at least send 'successful cancel' email to user + # but do we need to flash a message on the UI first? + redirect_to user_two_factor_authentication_url + end + + private + + def render_404_if_request_missing + render_not_found unless account_reset_request + end + + def account_reset_request + @account_reset_request ||= pending_account_reset_request(current_user) + end + end +end diff --git a/app/controllers/concerns/pending_account_reset_request_concern.rb b/app/controllers/concerns/pending_account_reset_request_concern.rb new file mode 100644 index 00000000000..bf87d08f0a4 --- /dev/null +++ b/app/controllers/concerns/pending_account_reset_request_concern.rb @@ -0,0 +1,11 @@ +module PendingAccountResetRequestConcern + extend ActiveSupport::Concern + + def pending_account_reset_request(user) + AccountResetRequest.where( + user_id: user.id, + granted_at: nil, + cancelled_at: nil + ).order(requested_at: :asc).first + end +end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 2e08cf4bba7..97034f9c278 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -4,6 +4,7 @@ class SessionsController < Devise::SessionsController # rubocop:disable Metrics/ include SecureHeadersConcern include RememberDeviceConcern include Ial2ProfileConcern + include PendingAccountResetRequestConcern rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_signin @@ -99,7 +100,9 @@ def handle_valid_authentication add_sp_cost(:digest) create_user_event(:sign_in_before_2fa) update_last_sign_in_at_on_email - redirect_to user_two_factor_authentication_url + redirect_to pending_account_reset_request(current_user) ? + account_reset_pending_url : + user_two_factor_authentication_url end def now diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb new file mode 100644 index 00000000000..e9c643eecdd --- /dev/null +++ b/app/presenters/account_reset/pending_presenter.rb @@ -0,0 +1,22 @@ + +require 'action_view' + +module AccountReset + class PendingPresenter + include ActionView::Helpers::DateHelper + + attr_reader :account_reset_request + attr_reader :time_remaining_until_granted + + def initialize(account_reset_request) + @account_reset_request = account_reset_request + @time_remaining_until_granted = time_remaining_until_granted + end + + def time_remaining_until_granted + interval = (account_reset_request.requested_at + 24.hours - Time.zone.now).round + duration = distance_of_time_in_words(interval).split("and") + duration[0].sub(",", " and ").sub(/minutes.*$/, "minutes").squeeze(" ") + end + end +end \ No newline at end of file diff --git a/app/views/account_reset/pending/show.html.erb b/app/views/account_reset/pending/show.html.erb new file mode 100644 index 00000000000..1df2646956e --- /dev/null +++ b/app/views/account_reset/pending/show.html.erb @@ -0,0 +1,9 @@ +

+ <%= 'You have requested an account reset' %> +

+ +

In <%= @pending_presenter.time_remaining_until_granted %> + you will be able to delete and recreate your account.

+ +

Click <%= link_to 'here', account_reset_pending_cancel_path(id: @pending_presenter.account_reset_request.id) %> + to cancel your account reset request.

diff --git a/config/routes.rb b/config/routes.rb index ba200a6f61e..3d2f825b8f7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -98,6 +98,8 @@ get '/account_reset/delete_account' => 'account_reset/delete_account#show' delete '/account_reset/delete_account' => 'account_reset/delete_account#delete' get '/account_reset/confirm_delete_account' => 'account_reset/confirm_delete_account#show' + get '/account_reset/pending' => 'account_reset/pending#show' + get '/account_reset/pending/cancel/:id' => 'account_reset/pending#cancel', as: :account_reset_pending_cancel get '/login/two_factor/options' => 'two_factor_authentication/options#index' post '/login/two_factor/options' => 'two_factor_authentication/options#create' diff --git a/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb b/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb new file mode 100644 index 00000000000..6fa14722f49 --- /dev/null +++ b/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe 'PendingAccountResetRequestConcern' do + include PendingAccountResetRequestConcern + include AccountResetHelper + + let(:user) { create(:user, :signed_up, email: 'old_email@example.com') } + + context '#pending_account_reset_request' do + it "returns a user's pending request" do + create_account_reset_request_for(user) + expect(pending_account_reset_request(user)).not_to eq nil + end + + it 'returns nil if the user has no pending request' do + create_account_reset_request_for(user) + AccountResetRequest.where(user_id: user.id).update_all( + cancelled_at: Time.zone.now, + granted_at: Time.zone.now - 10.minutes + ) + expect(pending_account_reset_request(user)).to eq nil + end + end +end diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index 93a1849d783..e333642039f 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -166,6 +166,8 @@ end describe 'POST /' do + include AccountResetHelper + it 'tracks the successful authentication for existing user' do user = create(:user, :signed_up) subject.session['user_return_to'] = 'http://example.com' @@ -430,6 +432,19 @@ post :create, params: { user: { email: user.email, password: user.password } } end end + + it 'redirects to 2FA if there are no pending account reset requests' do + user = create(:user, :signed_up) + post :create, params: { user: { email: user.email, password: user.password } } + expect(response).to redirect_to user_two_factor_authentication_url + end + + it 'redirects to the reset pending page if there are pending account reset requests' do + user = create(:user, :signed_up) + create_account_reset_request_for(user) + post :create, params: { user: { email: user.email, password: user.password } } + expect(response).to redirect_to account_reset_pending_url + end end describe '#new' do From 6849f7cff3a4eeca59e223b0d7dc1117b4008a49 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 20 May 2020 16:02:59 -0400 Subject: [PATCH 02/27] Cleanup **Why**: Rubocop --- app/controllers/account_reset/pending_controller.rb | 9 +++------ .../pending_account_reset_request_concern.rb | 2 +- app/controllers/users/sessions_controller.rb | 8 +++++--- app/presenters/account_reset/pending_presenter.rb | 13 +++++++------ .../pending_account_reset_request_concern_spec.rb | 4 +++- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 9c3de3b5d93..0a01280d00e 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -5,16 +5,13 @@ class PendingController < ApplicationController before_action :render_404_if_request_missing def show - analytics.track_event({ - event: 'account reset is pending', - user_id: current_user.uuid, - }) + analytics.track_event event: 'account reset is pending', user_id: current_user.uuid + @pending_presenter = AccountReset::PendingPresenter.new(account_reset_request) end def cancel - rec_to_cancel = AccountResetRequest.find_by_id(params[:id]) - pp rec_to_cancel + rec_to_cancel = AccountResetRequest.find_by(id: params[:id]) rec_to_cancel.cancelled_at = Time.zone.now rec_to_cancel.save! # this is wrong; needs to at least send 'successful cancel' email to user diff --git a/app/controllers/concerns/pending_account_reset_request_concern.rb b/app/controllers/concerns/pending_account_reset_request_concern.rb index bf87d08f0a4..11b6a3ba65b 100644 --- a/app/controllers/concerns/pending_account_reset_request_concern.rb +++ b/app/controllers/concerns/pending_account_reset_request_concern.rb @@ -5,7 +5,7 @@ def pending_account_reset_request(user) AccountResetRequest.where( user_id: user.id, granted_at: nil, - cancelled_at: nil + cancelled_at: nil, ).order(requested_at: :asc).first end end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 97034f9c278..73739386e58 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -100,9 +100,11 @@ def handle_valid_authentication add_sp_cost(:digest) create_user_event(:sign_in_before_2fa) update_last_sign_in_at_on_email - redirect_to pending_account_reset_request(current_user) ? - account_reset_pending_url : - user_two_factor_authentication_url + if pending_account_reset_request(current_user) + redirect_to account_reset_pending_url + else + redirect_to user_two_factor_authentication_url + end end def now diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb index e9c643eecdd..975bdde5624 100644 --- a/app/presenters/account_reset/pending_presenter.rb +++ b/app/presenters/account_reset/pending_presenter.rb @@ -1,4 +1,3 @@ - require 'action_view' module AccountReset @@ -10,13 +9,15 @@ class PendingPresenter def initialize(account_reset_request) @account_reset_request = account_reset_request - @time_remaining_until_granted = time_remaining_until_granted + @time_remaining_until_granted = time_remaining end - def time_remaining_until_granted + private + + def time_remaining interval = (account_reset_request.requested_at + 24.hours - Time.zone.now).round - duration = distance_of_time_in_words(interval).split("and") - duration[0].sub(",", " and ").sub(/minutes.*$/, "minutes").squeeze(" ") + duration = distance_of_time_in_words(interval).split('and') + duration[0].sub(',', ' and ').sub(/minutes.*$/, 'minutes').squeeze(' ') end end -end \ No newline at end of file +end diff --git a/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb b/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb index 6fa14722f49..e07b7c9485d 100644 --- a/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb +++ b/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb @@ -12,13 +12,15 @@ expect(pending_account_reset_request(user)).not_to eq nil end + # rubocop:disable Rails/SkipsModelValidations it 'returns nil if the user has no pending request' do create_account_reset_request_for(user) AccountResetRequest.where(user_id: user.id).update_all( cancelled_at: Time.zone.now, - granted_at: Time.zone.now - 10.minutes + granted_at: Time.zone.now - 10.minutes, ) expect(pending_account_reset_request(user)).to eq nil end + # rubocop:enable Rails/SkipsModelValidations end end From e57ed101e0c3f5362d4aab03afa72627930869ed Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 20 May 2020 16:36:14 -0400 Subject: [PATCH 03/27] More cleanup **Why**: Rubocop --- app/controllers/users/sessions_controller.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 73739386e58..ed260c2e82c 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -100,11 +100,7 @@ def handle_valid_authentication add_sp_cost(:digest) create_user_event(:sign_in_before_2fa) update_last_sign_in_at_on_email - if pending_account_reset_request(current_user) - redirect_to account_reset_pending_url - else - redirect_to user_two_factor_authentication_url - end + redirect_to_2fa_or_pending_reset(current_user) end def now @@ -166,5 +162,13 @@ def request_id def sp_session_ial sp_session[:ial2] ? 2 : 1 end + + def redirect_to_2fa_or_pending_reset(user) + if pending_account_reset_request(user) + redirect_to account_reset_pending_url + else + redirect_to user_two_factor_authentication_url + end + end end end From f82b753bfd6807685c5dd275a6631f0246516ae7 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 22 May 2020 10:12:50 -0400 Subject: [PATCH 04/27] Updated pending controller * Refactored presenter (rubocop) * Presenter returns duration down to 1 sec * Cancel click (from show) sends email * updated locales * updated specs **Why**: LG-2944 --- app/assets/stylesheets/email.css.scss | 5 +- .../account_reset/pending_controller.rb | 13 ++-- app/mailers/user_mailer.rb | 5 ++ .../account_reset/pending_presenter.rb | 33 +++++++-- app/views/account_reset/pending/show.html.erb | 12 +-- ...g_account_reset_request_cancelled.html.erb | 26 +++++++ config/locales/account_reset/en.yml | 5 ++ config/locales/account_reset/es.yml | 5 ++ config/locales/account_reset/fr.yml | 8 +- config/locales/misc/en.yml | 8 ++ config/locales/misc/es.yml | 8 ++ config/locales/misc/fr.yml | 8 ++ config/locales/time/en.yml | 8 +- config/locales/time/es.yml | 8 +- config/locales/time/fr.yml | 8 +- config/locales/user_mailer/en.yml | 8 ++ config/locales/user_mailer/es.yml | 8 ++ config/locales/user_mailer/fr.yml | 8 ++ config/routes.rb | 2 +- spec/mailers/user_mailer_spec.rb | 19 +++++ .../account_reset/pending_presenter_spec.rb | 73 +++++++++++++++++++ 21 files changed, 256 insertions(+), 22 deletions(-) create mode 100644 app/views/user_mailer/pending_account_reset_request_cancelled.html.erb create mode 100644 config/locales/misc/en.yml create mode 100644 config/locales/misc/es.yml create mode 100644 config/locales/misc/fr.yml create mode 100644 spec/presenters/account_reset/pending_presenter_spec.rb diff --git a/app/assets/stylesheets/email.css.scss b/app/assets/stylesheets/email.css.scss index d69531c8002..63ceef15222 100644 --- a/app/assets/stylesheets/email.css.scss +++ b/app/assets/stylesheets/email.css.scss @@ -13,7 +13,10 @@ } .hr { margin: $hr-margin; } -.lead { margin-bottom: 30px; } +.lead { + margin-bottom: 30px; + hyphens: none; +} .mr-tiny { margin-right: 4px; } .s10 { diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 0a01280d00e..9d4cef1a507 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -11,12 +11,15 @@ def show end def cancel - rec_to_cancel = AccountResetRequest.find_by(id: params[:id]) - rec_to_cancel.cancelled_at = Time.zone.now - rec_to_cancel.save! - # this is wrong; needs to at least send 'successful cancel' email to user - # but do we need to flash a message on the UI first? + # the record must be deleted b/c there's a unique user id constraint on the + # table, so we can't soft delete and add new requests for the same user + + # AccountResetRequest.where(user_id: current_user.id).delete_all + UserMailer.pending_account_reset_request_cancelled(current_user.email).deliver_now redirect_to user_two_factor_authentication_url + rescue StandardError + flash[:error] = t('account_reset.pending.cancel_error') + redirect_to account_reset_pending_url end private diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 5465c58724e..59628a16f22 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -88,6 +88,11 @@ def account_reset_cancel(email_address) mail(to: email_address.email, subject: t('user_mailer.account_reset_cancel.subject')) end + def pending_account_reset_request_cancelled(email_address) + mail(to: email_address, + subject: t('user_mailer.pending_account_reset_request_cancelled.subject')) + end + def please_reset_password(email_address, message) @message = message mail(to: email_address, subject: t('user_mailer.please_reset_password.subject')) diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb index 975bdde5624..7bc8014811c 100644 --- a/app/presenters/account_reset/pending_presenter.rb +++ b/app/presenters/account_reset/pending_presenter.rb @@ -1,23 +1,46 @@ -require 'action_view' - module AccountReset class PendingPresenter include ActionView::Helpers::DateHelper + include ActionView::Helpers::TranslationHelper attr_reader :account_reset_request attr_reader :time_remaining_until_granted def initialize(account_reset_request) @account_reset_request = account_reset_request - @time_remaining_until_granted = time_remaining + @time_remaining_until_granted = description(time_remaining) end private + def description(interval) + desc = distance_of_time_in_words(interval) + + # when words include hours/minutes/seconds, keep only hours/minutes + if desc.index('hour') && desc.index('second') + desc = desc[0, desc.index('minutes') + 'minutes'.length] + end + + # translate anything else (eg hours/min, min/seconds, seconds) as is + translate_desc(desc) + end + + def translate_desc(desc) + desc.gsub( + /(\,|and)/, " #{t('misc.and')} " + ).sub( + /hour(.)?/, t('time.hour') + '\1' + ).sub( + /minute(.)?/, t('time.minute') + '\1' + ).sub( + /second(.)?/, t('time.second') + '\1' + ).squeeze(' ') + end + def time_remaining + # go as low as 1 second interval = (account_reset_request.requested_at + 24.hours - Time.zone.now).round - duration = distance_of_time_in_words(interval).split('and') - duration[0].sub(',', ' and ').sub(/minutes.*$/, 'minutes').squeeze(' ') + interval < 1 ? 1 : interval end end end diff --git a/app/views/account_reset/pending/show.html.erb b/app/views/account_reset/pending/show.html.erb index 1df2646956e..98f959e948d 100644 --- a/app/views/account_reset/pending/show.html.erb +++ b/app/views/account_reset/pending/show.html.erb @@ -1,9 +1,11 @@

- <%= 'You have requested an account reset' %> + <%= t('account_reset.pending.header') %>

-

In <%= @pending_presenter.time_remaining_until_granted %> - you will be able to delete and recreate your account.

+

<%= t('misc.in').capitalize %> + <%= @pending_presenter.time_remaining_until_granted %>, + <%= t('account_reset.pending.delete_and_recreate') %>

-

Click <%= link_to 'here', account_reset_pending_cancel_path(id: @pending_presenter.account_reset_request.id) %> - to cancel your account reset request.

+

<%= t('misc.command.click') %> + <%= link_to t('misc.here'), account_reset_pending_cancel_path %> + <%= t('account_reset.pending.cancel') %>

diff --git a/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb b/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb new file mode 100644 index 00000000000..cccf15f0cbf --- /dev/null +++ b/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb @@ -0,0 +1,26 @@ +

<%= t('.intro', + app: link_to(APP_NAME, Figaro.env.mailer_domain_name, + class: 'gray')) %> +

+

<%= sanitize(t('.more_info')) %>

+ + + + + +
 
+ + + +
 
+ +

<%= 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/account_reset/en.yml b/config/locales/account_reset/en.yml index 2e31fda6210..40815ec396f 100644 --- a/config/locales/account_reset/en.yml +++ b/config/locales/account_reset/en.yml @@ -27,6 +27,11 @@ en: your account. Once your account is deleted, you can create a new one using the same email address. title: Deleting your account should be your last resort + pending: + cancel: to cancel your account reset request. + cancel_error: The request could not be cancelled. Please try again shortly. + delete_and_recreate: you will be able to delete and recreate your account. + header: You have requested an account reset request: access_your_account: access your account are_you_sure: Are you sure you don't have access to any of your authentication diff --git a/config/locales/account_reset/es.yml b/config/locales/account_reset/es.yml index f5731dd3b25..34ad07fe5b3 100644 --- a/config/locales/account_reset/es.yml +++ b/config/locales/account_reset/es.yml @@ -27,6 +27,11 @@ es: Una vez que se elimine su cuenta, puede crear una nueva usando la misma dirección de correo electrónico. title: Eliminar tu cuenta debería ser tu último recurso + pending: + cancel: para cancelar su solicitud de restablecimiento de cuenta. + cancel_error: La solicitud no pudo ser cancelada. Por favor intente nuevamente en breve. + delete_and_recreate: podrá eliminar y volver a crear su cuenta. + header: Has solicitado un restablecimiento de cuenta request: access_your_account: acceder a tu cuenta are_you_sure: "¿Estás seguro de que no tienes acceso a ninguno de tus métodos diff --git a/config/locales/account_reset/fr.yml b/config/locales/account_reset/fr.yml index b1305c0b96b..7ae140b2b75 100644 --- a/config/locales/account_reset/fr.yml +++ b/config/locales/account_reset/fr.yml @@ -2,8 +2,7 @@ fr: account_reset: cancel_request: - are_you_sure: Êtes-vous sûr de vouloir annuler votre demande de suppression - de compte? + are_you_sure: Êtes-vous sûr de vouloir annuler votre demande de suppression de compte? cancel: Sortie cancel_button: Annuler supprimer un compte title: Annuler supprimer un compte @@ -28,6 +27,11 @@ fr: liées à ton compte. Une fois votre compte supprimé, vous pouvez en créer un nouveau en utilisant la même adresse e-mail. title: La suppression de votre compte devrait être votre dernier recours + pending: + cancel: pour annuler votre demande de réinitialisation de compte. + cancel_error: La demande n'a pas pu être annulée. Veuillez réessayer sous peu. + delete_and_recreate: vous pourrez supprimer et recréer votre compte. + header: Vous avez demandé une réinitialisation de compte request: access_your_account: accéder à votre compte are_you_sure: Êtes-vous sûr de n'avoir accès à aucune de vos méthodes de sécurité? diff --git a/config/locales/misc/en.yml b/config/locales/misc/en.yml new file mode 100644 index 00000000000..f57274a65b9 --- /dev/null +++ b/config/locales/misc/en.yml @@ -0,0 +1,8 @@ +--- +en: + misc: + and: and + command: + click: Click + here: here + in: in diff --git a/config/locales/misc/es.yml b/config/locales/misc/es.yml new file mode 100644 index 00000000000..88e013371b5 --- /dev/null +++ b/config/locales/misc/es.yml @@ -0,0 +1,8 @@ +--- +es: + misc: + and: y + command: + click: Haga clic + here: aquí + in: en diff --git a/config/locales/misc/fr.yml b/config/locales/misc/fr.yml new file mode 100644 index 00000000000..704404d7748 --- /dev/null +++ b/config/locales/misc/fr.yml @@ -0,0 +1,8 @@ +--- +fr: + misc: + and: et + command: + click: Cliquez + here: ici + in: en diff --git a/config/locales/time/en.yml b/config/locales/time/en.yml index d5efd70acf9..3aaace101e6 100644 --- a/config/locales/time/en.yml +++ b/config/locales/time/en.yml @@ -2,7 +2,7 @@ en: date: month_names: - - + - - January - February - March @@ -20,4 +20,10 @@ en: formats: event_timestamp: "%B %-d, %Y at %-l:%M %p" event_timestamp_utc: "%B %-d, %Y at %-l:%M %p UTC" + hour: hour + hours: hours + minute: minute + minutes: minutes pm: PM + second: second + seconds: seconds diff --git a/config/locales/time/es.yml b/config/locales/time/es.yml index 4ed7e229f29..3d059033170 100644 --- a/config/locales/time/es.yml +++ b/config/locales/time/es.yml @@ -2,7 +2,7 @@ es: date: month_names: - - + - - enero - febrero - marzo @@ -20,4 +20,10 @@ es: formats: event_timestamp: "%e de %B de %Y a las %H:%M" event_timestamp_utc: "%e de %B de %Y a las %H:%M UTC" + hour: hora + hours: horas + minute: minuto + minutes: minutos pm: PM + second: segundo + seconds: segundos diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml index bcdd20aa47a..76c5906207d 100644 --- a/config/locales/time/fr.yml +++ b/config/locales/time/fr.yml @@ -2,7 +2,7 @@ fr: date: month_names: - - + - - janvier - février - mars @@ -20,4 +20,10 @@ fr: formats: event_timestamp: "%e %B %Y à %H:%M" event_timestamp_utc: "%e %B %Y à %H:%M UTC" + hour: heure + hours: heures + minute: minute + minutes: minutes pm: P.M. + second: seconde + seconds: secondes diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index ed52cd972ff..c28df28c3c4 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -105,6 +105,14 @@ en: help: If you did not make this change, you can reset your password %{disavowal_link}. For more help, please visit the %{app} %{help_link} or %{contact_link}. intro: You have a new password for your %{app} account. + pending_account_reset_request_cancelled: + header: Your pending account reset request has been cancelled + help: '' + intro: Earlier, you requeted this cancellation instead of choosing a second + form of authentication for your account. + more_info: "Although your account is still active, you must choose a + second form authentication to use it." + subject: Your pending account reset request has been cancelled personal_key_regenerated: help_html:

Your login.gov account was just issued a new 16-character personal key. You're getting this email to make sure it was you.

If you just diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index cb068f70f29..f39640fd035 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -108,6 +108,14 @@ es: help: Si no realizó este cambio puede restablecer su contraseña %{disavowal_link}. Para más ayuda, visite el %{app} %{help_link} o el %{contact_link}. intro: Tiene una contraseña nueva para su cuenta de %{app}. + pending_account_reset_request_cancelled: + header: Su solicitud de restablecimiento de cuenta pendiente ha sido cancelada + help: '' + intro: Anteriormente, solicitó esta cancelación en lugar de elegir una + segunda forma de autenticación. + more_info: "Aunque su cuenta aún está activa, debe elegir un +         autenticación de segundo formulario para usarlo. " + subject: Su solicitud de restablecimiento de cuenta pendiente ha sido cancelada personal_key_regenerated: help_html:

Tu cuenta de login.gov acaba de emitir una nueva clave personal de 16 caracteres. Estás recibiendo este correo electrónico para verificar diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index 311434143a5..c2eb9ecca2a 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -114,6 +114,14 @@ fr: votre mot de passe %{disavowal_link}. Pour plus d'aide, veuillez visiter le %{help_link} de %{app} ou %{contact_link}. intro: Le mot de passe de votre compte %{app} a été changé. + pending_account_reset_request_cancelled: + header: Votre demande de réinitialisation de compte en attente a été annulée + help: '' + intro: Plus tôt, vous avez demandé cette annulation au lieu de choisir une + deuxième forme d'authentification. + more_info: "Bien que votre compte soit toujours actif, vous devez choisir un +         authentification par deuxième formulaire pour l'utiliser." + subject: Votre demande de réinitialisation de compte en attente a été annulée personal_key_regenerated: help_html:

Votre compte login.gov vient de recevoir une nouvelle clé personnelle de 16 caractères. Le but de cet e-mail est de s'assurer que c'est bien vous diff --git a/config/routes.rb b/config/routes.rb index 3d2f825b8f7..ad494ba4e46 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,7 +99,7 @@ delete '/account_reset/delete_account' => 'account_reset/delete_account#delete' get '/account_reset/confirm_delete_account' => 'account_reset/confirm_delete_account#show' get '/account_reset/pending' => 'account_reset/pending#show' - get '/account_reset/pending/cancel/:id' => 'account_reset/pending#cancel', as: :account_reset_pending_cancel + get '/account_reset/pending/cancel' => 'account_reset/pending#cancel' get '/login/two_factor/options' => 'two_factor_authentication/options#index' post '/login/two_factor/options' => 'two_factor_authentication/options#create' diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 7268a77b68c..6f53c96dc85 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -335,6 +335,25 @@ def expect_email_body_to_have_help_and_contact_links end end + describe 'pending_account_reset_request_cancelled' do + let(:mail) { UserMailer.pending_account_reset_request_cancelled(user.email) } + + it_behaves_like 'a system email' + + it 'sends to the current email' do + expect(mail.to).to eq [user.email] + end + + it 'renders the subject' do + expect(mail.subject).to eq t('user_mailer.pending_account_reset_request_cancelled.subject') + end + + it 'renders the body' do + expect(mail.html_part.body). + to have_content(strip_tags(t('user_mailer.pending_account_reset_request_cancelled.intro'))) + end + end + describe 'please_reset_password' do let(:mail) { UserMailer.please_reset_password(email_address.email, 'This is a test.') } diff --git a/spec/presenters/account_reset/pending_presenter_spec.rb b/spec/presenters/account_reset/pending_presenter_spec.rb new file mode 100644 index 00000000000..87777b8d25a --- /dev/null +++ b/spec/presenters/account_reset/pending_presenter_spec.rb @@ -0,0 +1,73 @@ +require 'rails_helper' + +describe AccountReset::PendingPresenter do + include ActionView::Helpers::TranslationHelper + + describe '#account_reset_request' do + it 'returns account reset request' do + requested_at = Time.zone.now + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.account_reset_request.requested_at).to eq requested_at + end + end + + describe '#time_remaining_until_granted' do + let(:hour) { t('time.hour') } + let(:hours) { t('time.hours') } + let(:minute) { t('time.minute') } + let(:minutes) { t('time.minutes') } + let(:second) { t('time.second') } + let(:seconds) { t('time.seconds') } + + before { Timecop.freeze Time.zone.now } + after { Timecop.return } + + it 'returns time description in hours and minutes' do + requested_at = Time.zone.now - 20.5.hours + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "3 #{hours} and 30 #{minutes}" + + requested_at = Time.zone.now - 22.5.hours + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "1 #{hour} and 30 #{minutes}" + end + + it 'returns time description in hours and minutes, excluding seconds' do + requested_at = Time.zone.now - 20.hours - 3.seconds + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "3 #{hours} and 59 #{minutes}" + end + + it 'returns time description in minutes and seconds' do + requested_at = Time.zone.now - 24.hours + 70.seconds + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "1 #{minute} and 10 #{seconds}" + end + + it 'returns time description in seconds' do + requested_at = Time.zone.now - 24.hours + 30.seconds + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "30 #{seconds}" + end + + it 'returns time description as 1 second' do + requested_at = Time.zone.now - 24.hours + 1.second + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "1 #{second}" + end + + it 'returns time description as 1 second even if the remaining time is less than 1 second' do + requested_at = Time.zone.now - 24.hours + 0.5.seconds + account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) + presenter = described_class.new(account_reset_request) + expect(presenter.time_remaining_until_granted).to eq "1 #{second}" + end + end +end From 3447a516135e02b6db5cebe3560614756d44f4f6 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 22 May 2020 10:23:02 -0400 Subject: [PATCH 05/27] CSS tweak **Why**: Rubocop --- app/assets/stylesheets/email.css.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/email.css.scss b/app/assets/stylesheets/email.css.scss index 63ceef15222..3ecb10f43ea 100644 --- a/app/assets/stylesheets/email.css.scss +++ b/app/assets/stylesheets/email.css.scss @@ -13,10 +13,12 @@ } .hr { margin: $hr-margin; } + .lead { - margin-bottom: 30px; hyphens: none; + margin-bottom: 30px; } + .mr-tiny { margin-right: 4px; } .s10 { From 9c406ff46bafe19fdc80ba6c17e7b450cfa09e50 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 11:39:44 -0400 Subject: [PATCH 06/27] replace pending account reset cancel email with existing cancel email --- .../account_reset/pending_controller.rb | 9 +++---- app/mailers/user_mailer.rb | 5 ---- ...g_account_reset_request_cancelled.html.erb | 26 ------------------- config/locales/account_reset/es.yml | 3 ++- config/locales/account_reset/fr.yml | 3 ++- config/locales/time/en.yml | 2 +- config/locales/time/es.yml | 2 +- config/locales/time/fr.yml | 2 +- config/locales/user_mailer/en.yml | 8 ------ config/locales/user_mailer/es.yml | 8 ------ config/locales/user_mailer/fr.yml | 8 ------ spec/mailers/user_mailer_spec.rb | 19 -------------- 12 files changed, 11 insertions(+), 84 deletions(-) delete mode 100644 app/views/user_mailer/pending_account_reset_request_cancelled.html.erb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 9d4cef1a507..5b48ba54af4 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -11,11 +11,10 @@ def show end def cancel - # the record must be deleted b/c there's a unique user id constraint on the - # table, so we can't soft delete and add new requests for the same user - - # AccountResetRequest.where(user_id: current_user.id).delete_all - UserMailer.pending_account_reset_request_cancelled(current_user.email).deliver_now + AccountResetRequest.find_by(user_id: current_user.id).update(cancelled_at: Time.zone.now) + current_user.confirmed_email_addresses.each do |email_address| + UserMailer.account_reset_cancel(email_address).deliver_now + end redirect_to user_two_factor_authentication_url rescue StandardError flash[:error] = t('account_reset.pending.cancel_error') diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 59628a16f22..5465c58724e 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -88,11 +88,6 @@ def account_reset_cancel(email_address) mail(to: email_address.email, subject: t('user_mailer.account_reset_cancel.subject')) end - def pending_account_reset_request_cancelled(email_address) - mail(to: email_address, - subject: t('user_mailer.pending_account_reset_request_cancelled.subject')) - end - def please_reset_password(email_address, message) @message = message mail(to: email_address, subject: t('user_mailer.please_reset_password.subject')) diff --git a/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb b/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb deleted file mode 100644 index cccf15f0cbf..00000000000 --- a/app/views/user_mailer/pending_account_reset_request_cancelled.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -

<%= t('.intro', - app: link_to(APP_NAME, Figaro.env.mailer_domain_name, - class: 'gray')) %> -

-

<%= sanitize(t('.more_info')) %>

- - - - - -
 
- - - -
 
- -

<%= 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/account_reset/es.yml b/config/locales/account_reset/es.yml index 34ad07fe5b3..96add40b222 100644 --- a/config/locales/account_reset/es.yml +++ b/config/locales/account_reset/es.yml @@ -29,7 +29,8 @@ es: title: Eliminar tu cuenta debería ser tu último recurso pending: cancel: para cancelar su solicitud de restablecimiento de cuenta. - cancel_error: La solicitud no pudo ser cancelada. Por favor intente nuevamente en breve. + cancel_error: La solicitud no pudo ser cancelada. Por favor intente nuevamente + en breve. delete_and_recreate: podrá eliminar y volver a crear su cuenta. header: Has solicitado un restablecimiento de cuenta request: diff --git a/config/locales/account_reset/fr.yml b/config/locales/account_reset/fr.yml index 7ae140b2b75..a813c2a3309 100644 --- a/config/locales/account_reset/fr.yml +++ b/config/locales/account_reset/fr.yml @@ -2,7 +2,8 @@ fr: account_reset: cancel_request: - are_you_sure: Êtes-vous sûr de vouloir annuler votre demande de suppression de compte? + are_you_sure: Êtes-vous sûr de vouloir annuler votre demande de suppression + de compte? cancel: Sortie cancel_button: Annuler supprimer un compte title: Annuler supprimer un compte diff --git a/config/locales/time/en.yml b/config/locales/time/en.yml index 3aaace101e6..513b48738cc 100644 --- a/config/locales/time/en.yml +++ b/config/locales/time/en.yml @@ -2,7 +2,7 @@ en: date: month_names: - - + - - January - February - March diff --git a/config/locales/time/es.yml b/config/locales/time/es.yml index 3d059033170..de80bc5ace7 100644 --- a/config/locales/time/es.yml +++ b/config/locales/time/es.yml @@ -2,7 +2,7 @@ es: date: month_names: - - + - - enero - febrero - marzo diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml index 76c5906207d..5852fd7e1ef 100644 --- a/config/locales/time/fr.yml +++ b/config/locales/time/fr.yml @@ -2,7 +2,7 @@ fr: date: month_names: - - + - - janvier - février - mars diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index c28df28c3c4..ed52cd972ff 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -105,14 +105,6 @@ en: help: If you did not make this change, you can reset your password %{disavowal_link}. For more help, please visit the %{app} %{help_link} or %{contact_link}. intro: You have a new password for your %{app} account. - pending_account_reset_request_cancelled: - header: Your pending account reset request has been cancelled - help: '' - intro: Earlier, you requeted this cancellation instead of choosing a second - form of authentication for your account. - more_info: "Although your account is still active, you must choose a - second form authentication to use it." - subject: Your pending account reset request has been cancelled personal_key_regenerated: help_html:

Your login.gov account was just issued a new 16-character personal key. You're getting this email to make sure it was you.

If you just diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index f39640fd035..cb068f70f29 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -108,14 +108,6 @@ es: help: Si no realizó este cambio puede restablecer su contraseña %{disavowal_link}. Para más ayuda, visite el %{app} %{help_link} o el %{contact_link}. intro: Tiene una contraseña nueva para su cuenta de %{app}. - pending_account_reset_request_cancelled: - header: Su solicitud de restablecimiento de cuenta pendiente ha sido cancelada - help: '' - intro: Anteriormente, solicitó esta cancelación en lugar de elegir una - segunda forma de autenticación. - more_info: "Aunque su cuenta aún está activa, debe elegir un -         autenticación de segundo formulario para usarlo. " - subject: Su solicitud de restablecimiento de cuenta pendiente ha sido cancelada personal_key_regenerated: help_html:

Tu cuenta de login.gov acaba de emitir una nueva clave personal de 16 caracteres. Estás recibiendo este correo electrónico para verificar diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index c2eb9ecca2a..311434143a5 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -114,14 +114,6 @@ fr: votre mot de passe %{disavowal_link}. Pour plus d'aide, veuillez visiter le %{help_link} de %{app} ou %{contact_link}. intro: Le mot de passe de votre compte %{app} a été changé. - pending_account_reset_request_cancelled: - header: Votre demande de réinitialisation de compte en attente a été annulée - help: '' - intro: Plus tôt, vous avez demandé cette annulation au lieu de choisir une - deuxième forme d'authentification. - more_info: "Bien que votre compte soit toujours actif, vous devez choisir un -         authentification par deuxième formulaire pour l'utiliser." - subject: Votre demande de réinitialisation de compte en attente a été annulée personal_key_regenerated: help_html:

Votre compte login.gov vient de recevoir une nouvelle clé personnelle de 16 caractères. Le but de cet e-mail est de s'assurer que c'est bien vous diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 6f53c96dc85..7268a77b68c 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -335,25 +335,6 @@ def expect_email_body_to_have_help_and_contact_links end end - describe 'pending_account_reset_request_cancelled' do - let(:mail) { UserMailer.pending_account_reset_request_cancelled(user.email) } - - it_behaves_like 'a system email' - - it 'sends to the current email' do - expect(mail.to).to eq [user.email] - end - - it 'renders the subject' do - expect(mail.subject).to eq t('user_mailer.pending_account_reset_request_cancelled.subject') - end - - it 'renders the body' do - expect(mail.html_part.body). - to have_content(strip_tags(t('user_mailer.pending_account_reset_request_cancelled.intro'))) - end - end - describe 'please_reset_password' do let(:mail) { UserMailer.please_reset_password(email_address.email, 'This is a test.') } From 43dd744d1df5f216dc2c18a77e1c4dedae9225c3 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 11:41:09 -0400 Subject: [PATCH 07/27] quick cleanup --- app/controllers/account_reset/pending_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 5b48ba54af4..7c8435a7911 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -11,7 +11,7 @@ def show end def cancel - AccountResetRequest.find_by(user_id: current_user.id).update(cancelled_at: Time.zone.now) + account_reset_request.update(cancelled_at: Time.zone.now) current_user.confirmed_email_addresses.each do |email_address| UserMailer.account_reset_cancel(email_address).deliver_now end From d1389ddea340440c8ebcf46d3b690208da0dc05d Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 22 May 2020 11:55:02 -0400 Subject: [PATCH 08/27] Moved email to private method --- app/controllers/account_reset/pending_controller.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 7c8435a7911..a27f040c561 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -12,9 +12,7 @@ def show def cancel account_reset_request.update(cancelled_at: Time.zone.now) - current_user.confirmed_email_addresses.each do |email_address| - UserMailer.account_reset_cancel(email_address).deliver_now - end + send_cancellation_email redirect_to user_two_factor_authentication_url rescue StandardError flash[:error] = t('account_reset.pending.cancel_error') @@ -30,5 +28,11 @@ def render_404_if_request_missing def account_reset_request @account_reset_request ||= pending_account_reset_request(current_user) end + + def send_cancellation_email + current_user.confirmed_email_addresses.each do |email_address| + UserMailer.account_reset_cancel(email_address).deliver_now + end + end end end From da6b5f02e97a7578b6333beae21b1db6b4ed7c7a Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 12:44:57 -0400 Subject: [PATCH 09/27] Add a service for finding pending account reset requets --- .../account_reset/pending_controller.rb | 19 +++---- .../pending_account_reset_request_concern.rb | 8 +-- .../find_pending_request_for_user.rb | 20 ++++++++ .../find_prending_request_for_user_spec.rb | 49 +++++++++++++++++++ 4 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 app/services/account_reset/find_pending_request_for_user.rb create mode 100644 spec/services/account_reset/find_prending_request_for_user_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 7c8435a7911..2c73bd58441 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -2,29 +2,26 @@ module AccountReset class PendingController < ApplicationController include PendingAccountResetRequestConcern - before_action :render_404_if_request_missing + before_action :confirm_account_reset_request_exists def show analytics.track_event event: 'account reset is pending', user_id: current_user.uuid - @pending_presenter = AccountReset::PendingPresenter.new(account_reset_request) end def cancel - account_reset_request.update(cancelled_at: Time.zone.now) - current_user.confirmed_email_addresses.each do |email_address| - UserMailer.account_reset_cancel(email_address).deliver_now - end + AccountReset::CancelRequestForUser.new(user).call + # account_reset_request.update(cancelled_at: Time.zone.now) + # current_user.confirmed_email_addresses.each do |email_address| + # UserMailer.account_reset_cancel(email_address).deliver_now + # end redirect_to user_two_factor_authentication_url - rescue StandardError - flash[:error] = t('account_reset.pending.cancel_error') - redirect_to account_reset_pending_url end private - def render_404_if_request_missing - render_not_found unless account_reset_request + def confirm_account_reset_request_exists + render_not_found if account_reset_request.blank? end def account_reset_request diff --git a/app/controllers/concerns/pending_account_reset_request_concern.rb b/app/controllers/concerns/pending_account_reset_request_concern.rb index 11b6a3ba65b..553fb77c4a2 100644 --- a/app/controllers/concerns/pending_account_reset_request_concern.rb +++ b/app/controllers/concerns/pending_account_reset_request_concern.rb @@ -1,11 +1,5 @@ module PendingAccountResetRequestConcern extend ActiveSupport::Concern - def pending_account_reset_request(user) - AccountResetRequest.where( - user_id: user.id, - granted_at: nil, - cancelled_at: nil, - ).order(requested_at: :asc).first - end + def pending_account_reset_request(user); end end diff --git a/app/services/account_reset/find_pending_request_for_user.rb b/app/services/account_reset/find_pending_request_for_user.rb new file mode 100644 index 00000000000..3e3c0f262a3 --- /dev/null +++ b/app/services/account_reset/find_pending_request_for_user.rb @@ -0,0 +1,20 @@ +module AccountReset + class FindPendingRequestForUser + attr_reader :user + + def initialize(user) + @user = user + end + + def call + AccountResetRequest.where( + user: user, + granted_at: nil, + cancelled_at: nil, + ).where( + 'requested_at > ?', + Figaro.env.account_reset_wait_period_days.to_i.days.ago, + ).order(requested_at: :asc).first + end + end +end diff --git a/spec/services/account_reset/find_prending_request_for_user_spec.rb b/spec/services/account_reset/find_prending_request_for_user_spec.rb new file mode 100644 index 00000000000..beb1e742a1e --- /dev/null +++ b/spec/services/account_reset/find_prending_request_for_user_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +describe AccountReset::FindPendingRequestForUser do + describe '#call' do + let(:user) { create(:user) } + let(:granted_at) { nil } + let(:cancelled_at) { nil } + let(:requested_at) { 1.hour.ago } + + let!(:account_reset_request) do + AccountResetRequest.create( + user: user, + granted_at: granted_at, + cancelled_at: cancelled_at, + requested_at: requested_at, + ) + end + + subject { described_class.new(user) } + + context 'when a request exists' do + it { expect(subject.call).to eq(account_reset_request) } + end + + context 'when a request does not exist' do + let!(:account_reset_request) { nil } + + it { expect(subject.call).to be_nil } + end + + context 'when a request exists, but it has been granted' do + let(:granted_at) { 1.hour.ago } + + it { expect(subject.call).to be_nil } + end + + context 'when a request exists, but it is expired' do + let(:requested_at) { 1.year.ago } + + it { expect(subject.call).to be_nil } + end + + context 'when a request exists, but it has been cancelled' do + let(:cancelled_at) { 1.hour.ago } + + it { expect(subject.call).to be_nil } + end + end +end From 60c11e2ec83c09313930520b406ccbe4bc1047ff Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 12:47:49 -0400 Subject: [PATCH 10/27] Use the find pending request service --- .../account_reset/pending_controller.rb | 12 ++++----- .../pending_account_reset_request_concern.rb | 5 ---- app/controllers/users/sessions_controller.rb | 9 +++++-- ...ding_account_reset_request_concern_spec.rb | 26 ------------------- 4 files changed, 13 insertions(+), 39 deletions(-) delete mode 100644 app/controllers/concerns/pending_account_reset_request_concern.rb delete mode 100644 spec/controllers/concerns/pending_account_reset_request_concern_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 2c73bd58441..5b2197cb9b3 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -1,12 +1,10 @@ module AccountReset class PendingController < ApplicationController - include PendingAccountResetRequestConcern - before_action :confirm_account_reset_request_exists def show analytics.track_event event: 'account reset is pending', user_id: current_user.uuid - @pending_presenter = AccountReset::PendingPresenter.new(account_reset_request) + @pending_presenter = AccountReset::PendingPresenter.new(pending_account_reset_request) end def cancel @@ -21,11 +19,13 @@ def cancel private def confirm_account_reset_request_exists - render_not_found if account_reset_request.blank? + render_not_found if pending_account_reset_request.blank? end - def account_reset_request - @account_reset_request ||= pending_account_reset_request(current_user) + def pending_account_reset_request + @account_reset_request ||= AccountReset::FindPendingRequestForUser.new( + current_user, + ).call end end end diff --git a/app/controllers/concerns/pending_account_reset_request_concern.rb b/app/controllers/concerns/pending_account_reset_request_concern.rb deleted file mode 100644 index 553fb77c4a2..00000000000 --- a/app/controllers/concerns/pending_account_reset_request_concern.rb +++ /dev/null @@ -1,5 +0,0 @@ -module PendingAccountResetRequestConcern - extend ActiveSupport::Concern - - def pending_account_reset_request(user); end -end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index ed260c2e82c..7ba00ee8a22 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -4,7 +4,6 @@ class SessionsController < Devise::SessionsController # rubocop:disable Metrics/ include SecureHeadersConcern include RememberDeviceConcern include Ial2ProfileConcern - include PendingAccountResetRequestConcern rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_signin @@ -164,11 +163,17 @@ def sp_session_ial end def redirect_to_2fa_or_pending_reset(user) - if pending_account_reset_request(user) + if pending_account_reset_request.present? redirect_to account_reset_pending_url else redirect_to user_two_factor_authentication_url end end + + def pending_account_reset_request + AccountReset::FindPendingRequestForUser.new( + current_user, + ).call + end end end diff --git a/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb b/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb deleted file mode 100644 index e07b7c9485d..00000000000 --- a/spec/controllers/concerns/pending_account_reset_request_concern_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'rails_helper' - -describe 'PendingAccountResetRequestConcern' do - include PendingAccountResetRequestConcern - include AccountResetHelper - - let(:user) { create(:user, :signed_up, email: 'old_email@example.com') } - - context '#pending_account_reset_request' do - it "returns a user's pending request" do - create_account_reset_request_for(user) - expect(pending_account_reset_request(user)).not_to eq nil - end - - # rubocop:disable Rails/SkipsModelValidations - it 'returns nil if the user has no pending request' do - create_account_reset_request_for(user) - AccountResetRequest.where(user_id: user.id).update_all( - cancelled_at: Time.zone.now, - granted_at: Time.zone.now - 10.minutes, - ) - expect(pending_account_reset_request(user)).to eq nil - end - # rubocop:enable Rails/SkipsModelValidations - end -end From ea94b6723a98b00e2b03932255959ff6b31afbe2 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 13:05:39 -0400 Subject: [PATCH 11/27] Add a cancel request service --- .../account_reset/cancel_request_for_user.rb | 19 ++++++++++++ .../cancel_request_for_user_spec.rb | 29 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 app/services/account_reset/cancel_request_for_user.rb create mode 100644 spec/services/account_reset/cancel_request_for_user_spec.rb diff --git a/app/services/account_reset/cancel_request_for_user.rb b/app/services/account_reset/cancel_request_for_user.rb new file mode 100644 index 00000000000..8bd384295be --- /dev/null +++ b/app/services/account_reset/cancel_request_for_user.rb @@ -0,0 +1,19 @@ +module AccountReset + class CancelRequestForUser + attr_reader :user + + def initialize(user) + @user = user + end + + def call + account_reset_request.update!(cancelled_at: Time.zone.now) + end + + private + + def account_reset_request + FindPendingRequestForUser.new(user).call + end + end +end diff --git a/spec/services/account_reset/cancel_request_for_user_spec.rb b/spec/services/account_reset/cancel_request_for_user_spec.rb new file mode 100644 index 00000000000..c6f4c67ab6c --- /dev/null +++ b/spec/services/account_reset/cancel_request_for_user_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +describe AccountReset::CancelRequestForUser do + let(:user) { create(:user) } + let!(:account_reset_request) { AccountResetRequest.create(user: user, requested_at: 1.hour.ago) } + + subject { described_class.new(user) } + + describe '#call' do + it 'cancels the account reset request' do + subject.call + + expect(account_reset_request.reload.cancelled_at).to be_within(1.second).of(Time.zone.now) + end + + it 'does not cancel account reset requests for a different user' do + other_user = create(:user) + other_request = AccountResetRequest.create(user: other_user, requested_at: 1.hour.ago) + + subject.call + + expect(other_request.reload.cancelled_at).to be_nil + end + + it "sends an email to the user's confirmed email addresses" do + + end + end +end From bcd7b8f520df1c7e1560370fc030efb3e98135d2 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 13:38:07 -0400 Subject: [PATCH 12/27] Move cancelled notification into a service --- .../account_reset/pending_controller.rb | 1 - app/services/account_reset/cancel.rb | 17 +-------- .../account_reset/cancel_request_for_user.rb | 1 + .../notify_user_of_request_cancellation.rb | 28 ++++++++++++++ .../cancel_request_for_user_spec.rb | 6 +++ ...otify_user_of_request_cancellation_spec.rb | 37 +++++++++++++++++++ 6 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 app/services/account_reset/notify_user_of_request_cancellation.rb create mode 100644 spec/services/account_reset/notify_user_of_request_cancellation_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 5b2197cb9b3..989a04d1666 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -9,7 +9,6 @@ def show def cancel AccountReset::CancelRequestForUser.new(user).call - # account_reset_request.update(cancelled_at: Time.zone.now) # current_user.confirmed_email_addresses.each do |email_address| # UserMailer.account_reset_cancel(email_address).deliver_now # end diff --git a/app/services/account_reset/cancel.rb b/app/services/account_reset/cancel.rb index 98b7907e818..ead086cfcac 100644 --- a/app/services/account_reset/cancel.rb +++ b/app/services/account_reset/cancel.rb @@ -11,8 +11,7 @@ def call @success = valid? if success - notify_user_via_email_of_account_reset_cancellation - notify_user_via_phone_of_account_reset_cancellation if phone.present? + AccountReset::NotifyUserOfRequestCancellation.new(user).call update_account_reset_request end @@ -23,16 +22,6 @@ def call attr_reader :success, :token - def notify_user_via_email_of_account_reset_cancellation - user.confirmed_email_addresses.each do |email_address| - UserMailer.account_reset_cancel(email_address).deliver_later - end - end - - def notify_user_via_phone_of_account_reset_cancellation - Telephony.send_account_reset_cancellation_notice(to: phone) - end - def update_account_reset_request account_reset_request.update!(cancelled_at: Time.zone.now, request_token: nil, @@ -43,10 +32,6 @@ def user account_reset_request&.user || AnonymousUser.new end - def phone - MfaContext.new(user).phone_configurations.take&.phone - end - def extra_analytics_attributes { event: 'cancel', diff --git a/app/services/account_reset/cancel_request_for_user.rb b/app/services/account_reset/cancel_request_for_user.rb index 8bd384295be..7f0fa117538 100644 --- a/app/services/account_reset/cancel_request_for_user.rb +++ b/app/services/account_reset/cancel_request_for_user.rb @@ -8,6 +8,7 @@ def initialize(user) def call account_reset_request.update!(cancelled_at: Time.zone.now) + NotifyUserOfRequestCancellation.new(user).call end private diff --git a/app/services/account_reset/notify_user_of_request_cancellation.rb b/app/services/account_reset/notify_user_of_request_cancellation.rb new file mode 100644 index 00000000000..beb12517c8b --- /dev/null +++ b/app/services/account_reset/notify_user_of_request_cancellation.rb @@ -0,0 +1,28 @@ +module AccountReset + class NotifyUserOfRequestCancellation + attr_reader :user + + def initialize(user) + @user = user + end + + def call + notify_user_via_email_of_account_reset_cancellation + notify_user_via_phone_of_account_reset_cancellation + end + + private + + def notify_user_via_email_of_account_reset_cancellation + user.confirmed_email_addresses.each do |email_address| + UserMailer.account_reset_cancel(email_address).deliver_later + end + end + + def notify_user_via_phone_of_account_reset_cancellation + MfaContext.new(user).phone_configurations.each do |phone_configuration| + Telephony.send_account_reset_cancellation_notice(to: phone_configuration.phone) + end + end + end +end diff --git a/spec/services/account_reset/cancel_request_for_user_spec.rb b/spec/services/account_reset/cancel_request_for_user_spec.rb index c6f4c67ab6c..9768fb6f6d1 100644 --- a/spec/services/account_reset/cancel_request_for_user_spec.rb +++ b/spec/services/account_reset/cancel_request_for_user_spec.rb @@ -23,7 +23,13 @@ end it "sends an email to the user's confirmed email addresses" do + notify_user_of_cancellation = instance_double(AccountReset::NotifyUserOfRequestCancellation) + expect(AccountReset::NotifyUserOfRequestCancellation).to receive(:new). + with(user). + and_return(notify_user_of_cancellation) + expect(notify_user_of_cancellation).to receive(:call) + subject.call end end end diff --git a/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb b/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb new file mode 100644 index 00000000000..a48db9c2a0f --- /dev/null +++ b/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb @@ -0,0 +1,37 @@ +require 'rails_helper' + +describe AccountReset::NotifyUserOfRequestCancellation do + let(:user) { create(:user) } + + subject { described_class.new(user) } + + describe '#call' do + it 'sends an email to all of the user email addresses' do + email_address1 = user.email_addresses.first + email_address2 = create(:email_address, user: user) + + mail1 = double + mail2 = double + + expect(UserMailer).to receive(:account_reset_cancel).with(email_address1).and_return(mail1) + expect(UserMailer).to receive(:account_reset_cancel).with(email_address2).and_return(mail2) + + expect(mail1).to receive(:deliver_later) + expect(mail2).to receive(:deliver_later) + + subject.call + end + + it 'sends a text to all of the user phone numbers' do + phone_config1 = create(:phone_configuration, user: user) + phone_config2 = create(:phone_configuration, user: user) + + expect(Telephony).to receive(:send_account_reset_cancellation_notice). + with(to: phone_config1.phone) + expect(Telephony).to receive(:send_account_reset_cancellation_notice). + with(to: phone_config2.phone) + + subject.call + end + end +end From af0edc3767760e844ba5e3f768aa9b980f1520b1 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 13:51:43 -0400 Subject: [PATCH 13/27] start writing controller specs --- .../account_reset/pending_controller.rb | 9 +++++---- app/services/analytics.rb | 2 ++ .../account_reset/pending_controller_spec.rb | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 spec/controllers/account_reset/pending_controller_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 989a04d1666..04f68aa69c8 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -1,17 +1,18 @@ module AccountReset class PendingController < ApplicationController + include UserAuthenticator + + before_action :authenticate_user before_action :confirm_account_reset_request_exists def show - analytics.track_event event: 'account reset is pending', user_id: current_user.uuid + analytics.track_event(Analytics::PENDING_ACCOUNT_RESET_VISITED) @pending_presenter = AccountReset::PendingPresenter.new(pending_account_reset_request) end def cancel + analytics.track_event(Analytics::PENDING_ACCOUNT_RESET_CANCELLED) AccountReset::CancelRequestForUser.new(user).call - # current_user.confirmed_email_addresses.each do |email_address| - # UserMailer.account_reset_cancel(email_address).deliver_now - # end redirect_to user_two_factor_authentication_url end diff --git a/app/services/analytics.rb b/app/services/analytics.rb index 5f473bf9d53..bc895e2cd52 100644 --- a/app/services/analytics.rb +++ b/app/services/analytics.rb @@ -151,6 +151,8 @@ def browser_attributes PASSWORD_RESET_PASSWORD = 'Password Reset: Password Submitted'.freeze PASSWORD_RESET_TOKEN = 'Password Reset: Token Submitted'.freeze PASSWORD_RESET_VISIT = 'Password Reset: Email Form Visited'.freeze + PENDING_ACCOUNT_RESET_CANCELLED = 'Pending account reset cancelled'.friize + PENDING_ACCOUNT_RESET_VISITED = 'Pending account reset visited'.freeze PERSONAL_KEY_REACTIVATION = 'Personal key reactivation: Account reactivated with personal key'.freeze PERSONAL_KEY_REACTIVATION_SIGN_IN = 'Personal key reactivation: Account reactivated with personal key as MFA'.freeze PERSONAL_KEY_REACTIVATION_SUBMITTED = 'Personal key reactivation: Personal key form submitted'.freeze diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb new file mode 100644 index 00000000000..db051eb8c3f --- /dev/null +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +describe AccountReset::PendingController do + descirbe '#show' do + it 'renders' + + context 'when the account reset request does not exist' + it 'renders a 404' + end + end + + describe '#cancel' do + it 'cancels the account reset request' + + context 'when the account reset request does not exist' do + it 'renders a 404' + end + end +end From e21e8ed43b7764bebece63d3037bad35c8a2bb02 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:02:31 -0400 Subject: [PATCH 14/27] cleanup pending presenter --- .../account_reset/pending_presenter.rb | 39 ++------ app/services/analytics.rb | 2 +- config/locales/dotiw/en.yml | 2 +- config/locales/dotiw/es.yml | 2 +- config/locales/dotiw/fr.yml | 2 +- config/locales/time/en.yml | 36 +++---- config/locales/time/es.yml | 36 +++---- config/locales/time/fr.yml | 36 +++---- .../account_reset/pending_controller_spec.rb | 2 +- .../account_reset/pending_presenter_spec.rb | 96 +++++++++---------- 10 files changed, 102 insertions(+), 151 deletions(-) diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb index 7bc8014811c..cf9de0dbadc 100644 --- a/app/presenters/account_reset/pending_presenter.rb +++ b/app/presenters/account_reset/pending_presenter.rb @@ -4,43 +4,18 @@ class PendingPresenter include ActionView::Helpers::TranslationHelper attr_reader :account_reset_request - attr_reader :time_remaining_until_granted def initialize(account_reset_request) @account_reset_request = account_reset_request - @time_remaining_until_granted = description(time_remaining) end - private - - def description(interval) - desc = distance_of_time_in_words(interval) - - # when words include hours/minutes/seconds, keep only hours/minutes - if desc.index('hour') && desc.index('second') - desc = desc[0, desc.index('minutes') + 'minutes'.length] - end - - # translate anything else (eg hours/min, min/seconds, seconds) as is - translate_desc(desc) - end - - def translate_desc(desc) - desc.gsub( - /(\,|and)/, " #{t('misc.and')} " - ).sub( - /hour(.)?/, t('time.hour') + '\1' - ).sub( - /minute(.)?/, t('time.minute') + '\1' - ).sub( - /second(.)?/, t('time.second') + '\1' - ).squeeze(' ') - end - - def time_remaining - # go as low as 1 second - interval = (account_reset_request.requested_at + 24.hours - Time.zone.now).round - interval < 1 ? 1 : interval + def time_remaining_until_granted + distance_of_time_in_words( + Time.zone.now, + account_reset_request.requested_at + Figaro.env.account_reset_wait_period_days.to_i.days, + true, + highest_measures: 2, + ) end end end diff --git a/app/services/analytics.rb b/app/services/analytics.rb index bc895e2cd52..7115934cc8a 100644 --- a/app/services/analytics.rb +++ b/app/services/analytics.rb @@ -151,7 +151,7 @@ def browser_attributes PASSWORD_RESET_PASSWORD = 'Password Reset: Password Submitted'.freeze PASSWORD_RESET_TOKEN = 'Password Reset: Token Submitted'.freeze PASSWORD_RESET_VISIT = 'Password Reset: Email Form Visited'.freeze - PENDING_ACCOUNT_RESET_CANCELLED = 'Pending account reset cancelled'.friize + PENDING_ACCOUNT_RESET_CANCELLED = 'Pending account reset cancelled'.freeze PENDING_ACCOUNT_RESET_VISITED = 'Pending account reset visited'.freeze PERSONAL_KEY_REACTIVATION = 'Personal key reactivation: Account reactivated with personal key'.freeze PERSONAL_KEY_REACTIVATION_SIGN_IN = 'Personal key reactivation: Account reactivated with personal key as MFA'.freeze diff --git a/config/locales/dotiw/en.yml b/config/locales/dotiw/en.yml index f5c277acf76..19a42c76d37 100644 --- a/config/locales/dotiw/en.yml +++ b/config/locales/dotiw/en.yml @@ -2,4 +2,4 @@ en: datetime: dotiw: - two_words_connector: and + two_words_connector: ' and ' diff --git a/config/locales/dotiw/es.yml b/config/locales/dotiw/es.yml index 0c407bd25c2..b0a2e574dde 100644 --- a/config/locales/dotiw/es.yml +++ b/config/locales/dotiw/es.yml @@ -2,4 +2,4 @@ es: datetime: dotiw: - two_words_connector: y + two_words_connector: ' y ' diff --git a/config/locales/dotiw/fr.yml b/config/locales/dotiw/fr.yml index d435f590725..3892ac212e1 100644 --- a/config/locales/dotiw/fr.yml +++ b/config/locales/dotiw/fr.yml @@ -2,4 +2,4 @@ fr: datetime: dotiw: - two_words_connector: et + two_words_connector: ' et ' diff --git a/config/locales/time/en.yml b/config/locales/time/en.yml index 513b48738cc..3a723174c61 100644 --- a/config/locales/time/en.yml +++ b/config/locales/time/en.yml @@ -2,28 +2,22 @@ en: date: month_names: - - - - January - - February - - March - - April - - May - - June - - July - - August - - September - - October - - November - - December + - + - January + - February + - March + - April + - May + - June + - July + - August + - September + - October + - November + - December time: am: AM formats: - event_timestamp: "%B %-d, %Y at %-l:%M %p" - event_timestamp_utc: "%B %-d, %Y at %-l:%M %p UTC" - hour: hour - hours: hours - minute: minute - minutes: minutes + event_timestamp: '%B %-d, %Y at %-l:%M %p' + event_timestamp_utc: '%B %-d, %Y at %-l:%M %p UTC' pm: PM - second: second - seconds: seconds diff --git a/config/locales/time/es.yml b/config/locales/time/es.yml index de80bc5ace7..5a1f19ba2de 100644 --- a/config/locales/time/es.yml +++ b/config/locales/time/es.yml @@ -2,28 +2,22 @@ es: date: month_names: - - - - enero - - febrero - - marzo - - abril - - mayo - - junio - - julio - - agosto - - septiembre - - octubre - - noviembre - - diciembre + - + - enero + - febrero + - marzo + - abril + - mayo + - junio + - julio + - agosto + - septiembre + - octubre + - noviembre + - diciembre time: am: AM formats: - event_timestamp: "%e de %B de %Y a las %H:%M" - event_timestamp_utc: "%e de %B de %Y a las %H:%M UTC" - hour: hora - hours: horas - minute: minuto - minutes: minutos + event_timestamp: '%e de %B de %Y a las %H:%M' + event_timestamp_utc: '%e de %B de %Y a las %H:%M UTC' pm: PM - second: segundo - seconds: segundos diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml index 5852fd7e1ef..ac1a82789eb 100644 --- a/config/locales/time/fr.yml +++ b/config/locales/time/fr.yml @@ -2,28 +2,22 @@ fr: date: month_names: - - - - janvier - - février - - mars - - avril - - mai - - juin - - juillet - - août - - septembre - - octobre - - novembre - - décembre + - + - janvier + - février + - mars + - avril + - mai + - juin + - juillet + - août + - septembre + - octobre + - novembre + - décembre time: am: A.M. formats: - event_timestamp: "%e %B %Y à %H:%M" - event_timestamp_utc: "%e %B %Y à %H:%M UTC" - hour: heure - hours: heures - minute: minute - minutes: minutes + event_timestamp: '%e %B %Y à %H:%M' + event_timestamp_utc: '%e %B %Y à %H:%M UTC' pm: P.M. - second: seconde - seconds: secondes diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb index db051eb8c3f..f673018a963 100644 --- a/spec/controllers/account_reset/pending_controller_spec.rb +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -4,7 +4,7 @@ descirbe '#show' do it 'renders' - context 'when the account reset request does not exist' + context 'when the account reset request does not exist' do it 'renders a 404' end end diff --git a/spec/presenters/account_reset/pending_presenter_spec.rb b/spec/presenters/account_reset/pending_presenter_spec.rb index 87777b8d25a..5e96b979d74 100644 --- a/spec/presenters/account_reset/pending_presenter_spec.rb +++ b/spec/presenters/account_reset/pending_presenter_spec.rb @@ -1,73 +1,67 @@ require 'rails_helper' describe AccountReset::PendingPresenter do - include ActionView::Helpers::TranslationHelper + let(:user) { create(:user) } + let(:hours) { 21 } + let(:minutes) { 13 } + let(:seconds) { 40 } + let(:requested_at) do + 24.hours.ago + hours.hours + minutes.minutes + seconds.seconds + end + let(:account_reset_request) do + AccountResetRequest.new( + user: user, + requested_at: requested_at, + ) + end + + subject { described_class.new(account_reset_request) } describe '#account_reset_request' do it 'returns account reset request' do - requested_at = Time.zone.now - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.account_reset_request.requested_at).to eq requested_at + expect(subject.account_reset_request.requested_at).to eq(requested_at) end end describe '#time_remaining_until_granted' do - let(:hour) { t('time.hour') } - let(:hours) { t('time.hours') } - let(:minute) { t('time.minute') } - let(:minutes) { t('time.minutes') } - let(:second) { t('time.second') } - let(:seconds) { t('time.seconds') } - - before { Timecop.freeze Time.zone.now } - after { Timecop.return } - - it 'returns time description in hours and minutes' do - requested_at = Time.zone.now - 20.5.hours - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "3 #{hours} and 30 #{minutes}" - - requested_at = Time.zone.now - 22.5.hours - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "1 #{hour} and 30 #{minutes}" + around(:each) do |example| + I18n.locale = :en + Timecop.freeze Time.zone.now do + example.run + end end - it 'returns time description in hours and minutes, excluding seconds' do - requested_at = Time.zone.now - 20.hours - 3.seconds - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "3 #{hours} and 59 #{minutes}" + context 'when requested at is greater than an hour' do + it 'returns the description in hours and minutes' do + expect(subject.time_remaining_until_granted).to eq '21 hours and 13 minutes' + end end - it 'returns time description in minutes and seconds' do - requested_at = Time.zone.now - 24.hours + 70.seconds - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "1 #{minute} and 10 #{seconds}" - end + context 'when requested at is greater than a minute' do + let(:hours) { 0 } - it 'returns time description in seconds' do - requested_at = Time.zone.now - 24.hours + 30.seconds - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "30 #{seconds}" + it 'returns the description in minutes and seconds' do + expect(subject.time_remaining_until_granted).to eq '13 minutes and 40 seconds' + end end - it 'returns time description as 1 second' do - requested_at = Time.zone.now - 24.hours + 1.second - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "1 #{second}" + context 'when requested at is less than a minute' do + let(:hours) { 0 } + let(:minutes) { 0 } + + it 'returns the description in minutes and seconds' do + expect(subject.time_remaining_until_granted).to eq '40 seconds' + end end - it 'returns time description as 1 second even if the remaining time is less than 1 second' do - requested_at = Time.zone.now - 24.hours + 0.5.seconds - account_reset_request = AccountResetRequest.new(user_id: 1, requested_at: requested_at) - presenter = described_class.new(account_reset_request) - expect(presenter.time_remaining_until_granted).to eq "1 #{second}" + context 'when the request is less than a second' do + let(:hours) { 0 } + let(:minutes) { 0 } + let(:seconds) { 0 } + + it 'returns the description in minutes and seconds' do + expect(subject.time_remaining_until_granted).to eq 'less than 1 second' + end end end end From b073f94bb241a0b04a8707a68abea52140c51bc8 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:03:51 -0400 Subject: [PATCH 15/27] fix lint issue --- app/controllers/users/sessions_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 7ba00ee8a22..19c623ce4f8 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -99,7 +99,7 @@ def handle_valid_authentication add_sp_cost(:digest) create_user_event(:sign_in_before_2fa) update_last_sign_in_at_on_email - redirect_to_2fa_or_pending_reset(current_user) + redirect_to_2fa_or_pending_reset end def now @@ -162,7 +162,7 @@ def sp_session_ial sp_session[:ial2] ? 2 : 1 end - def redirect_to_2fa_or_pending_reset(user) + def redirect_to_2fa_or_pending_reset if pending_account_reset_request.present? redirect_to account_reset_pending_url else From 98a100e4d144ecfa286007358b0479ff25618b7f Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:21:13 -0400 Subject: [PATCH 16/27] cleanup the template --- .../account_reset/pending_controller.rb | 2 +- app/views/account_reset/pending/show.html.erb | 23 ++++++++++---- config/locales/account_reset/en.yml | 7 +++-- config/locales/account_reset/es.yml | 29 +++++++++--------- config/locales/account_reset/fr.yml | 6 ++-- config/locales/dotiw/en.yml | 2 +- config/locales/dotiw/es.yml | 2 +- config/locales/dotiw/fr.yml | 2 +- config/locales/misc/en.yml | 8 ----- config/locales/misc/es.yml | 8 ----- config/locales/misc/fr.yml | 8 ----- config/locales/time/en.yml | 30 +++++++++---------- config/locales/time/es.yml | 30 +++++++++---------- config/locales/time/fr.yml | 30 +++++++++---------- config/routes.rb | 2 +- 15 files changed, 89 insertions(+), 100 deletions(-) delete mode 100644 config/locales/misc/en.yml delete mode 100644 config/locales/misc/es.yml delete mode 100644 config/locales/misc/fr.yml diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 04f68aa69c8..1dbc415ada3 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -12,7 +12,7 @@ def show def cancel analytics.track_event(Analytics::PENDING_ACCOUNT_RESET_CANCELLED) - AccountReset::CancelRequestForUser.new(user).call + AccountReset::CancelRequestForUser.new(current_user).call redirect_to user_two_factor_authentication_url end diff --git a/app/views/account_reset/pending/show.html.erb b/app/views/account_reset/pending/show.html.erb index 98f959e948d..5bc207a194c 100644 --- a/app/views/account_reset/pending/show.html.erb +++ b/app/views/account_reset/pending/show.html.erb @@ -2,10 +2,21 @@ <%= t('account_reset.pending.header') %> -

<%= t('misc.in').capitalize %> - <%= @pending_presenter.time_remaining_until_granted %>, - <%= t('account_reset.pending.delete_and_recreate') %>

+

+ <%= t( + 'account_reset.pending.delete_and_recreate_html', + interval: @pending_presenter.time_remaining_until_granted, + ) %> +

-

<%= t('misc.command.click') %> - <%= link_to t('misc.here'), account_reset_pending_cancel_path %> - <%= t('account_reset.pending.cancel') %>

+

+ <%= t( + 'account_reset.pending.cancel_html', + click_here: button_to( + t('account_reset.pending.click_here'), + account_reset_pending_cancel_path, + class: 'btn btn-link', + form_class: 'display-inline', + ), + ) %> +

diff --git a/config/locales/account_reset/en.yml b/config/locales/account_reset/en.yml index 40815ec396f..14c3406f8ea 100644 --- a/config/locales/account_reset/en.yml +++ b/config/locales/account_reset/en.yml @@ -28,9 +28,10 @@ en: the same email address. title: Deleting your account should be your last resort pending: - cancel: to cancel your account reset request. - cancel_error: The request could not be cancelled. Please try again shortly. - delete_and_recreate: you will be able to delete and recreate your account. + cancel_html: "%{click_here} to cancel your account reset request." + click_here: Click here + delete_and_recreate_html: In %{interval} you will be able to + delete and recreate your account. header: You have requested an account reset request: access_your_account: access your account diff --git a/config/locales/account_reset/es.yml b/config/locales/account_reset/es.yml index 96add40b222..dab8b1128fb 100644 --- a/config/locales/account_reset/es.yml +++ b/config/locales/account_reset/es.yml @@ -22,27 +22,28 @@ es: a su registro número de teléfono. delete_account: are_you_sure: "¿Seguro que quieres eliminar tu cuenta?" - info: Eliminar su cuenta debe ser su último recurso si está bloqueado          - de tu cuenta No podrá recuperar ninguna información vinculada a su cuenta. - Una vez que se elimine su cuenta, puede crear una nueva usando la misma dirección + info: Eliminar su cuenta debe ser su último recurso si está bloqueado de tu + cuenta No podrá recuperar ninguna información vinculada a su cuenta. Una vez + que se elimine su cuenta, puede crear una nueva usando la misma dirección de correo electrónico. title: Eliminar tu cuenta debería ser tu último recurso pending: - cancel: para cancelar su solicitud de restablecimiento de cuenta. - cancel_error: La solicitud no pudo ser cancelada. Por favor intente nuevamente - en breve. - delete_and_recreate: podrá eliminar y volver a crear su cuenta. + cancel_html: "%{click_here} para cancelar su solicitud de restablecimiento de + cuenta." + click_here: Haga clic aquí + delete_and_recreate_html: En %{interval} podrá eliminar y volver + a crear su cuenta. header: Has solicitado un restablecimiento de cuenta request: access_your_account: acceder a tu cuenta are_you_sure: "¿Estás seguro de que no tienes acceso a ninguno de tus métodos de seguridad?" delete_account: Eliminar su cuenta - delete_account_info: Eliminar su cuenta existente y crear una nueva          - le permite usar la misma dirección de correo electrónico y configurar nuevas - opciones de seguridad. Sin embargo, eliminar eliminará cualquier aplicación - de agencia que haya vinculado a su cuenta y deberá restaurar cada conexión. -

Si continúas, tú primero recibirá una confirmación por correo electrónico. + delete_account_info: Eliminar su cuenta existente y crear una nueva le permite + usar la misma dirección de correo electrónico y configurar nuevas opciones + de seguridad. Sin embargo, eliminar eliminará cualquier aplicación de agencia + que haya vinculado a su cuenta y deberá restaurar cada conexión.

+ Si continúas, tú primero recibirá una confirmación por correo electrónico. Como medida de seguridad, lo hará reciba otro correo electrónico con el enlace para seguir eliminando su cuenta las 24 horas después del correo electrónico de confirmación inicial llega. @@ -55,7 +56,7 @@ es: personal_key_info: Su clave personal es un código de 16 caracteres que se le dio a en la creación de la cuenta como método de recuperación; consulte el ejemplo a continuación. - personal_key_trailer: Si tiene su clave personal, puede usarla en %{link}          - en lugar de restablecer. + personal_key_trailer: Si tiene su clave personal, puede usarla en %{link} en + lugar de restablecer. title: Eliminación y restablecimiento de cuenta yes_continue: Sí, continúa la eliminación. diff --git a/config/locales/account_reset/fr.yml b/config/locales/account_reset/fr.yml index a813c2a3309..a076cd2d668 100644 --- a/config/locales/account_reset/fr.yml +++ b/config/locales/account_reset/fr.yml @@ -29,9 +29,9 @@ fr: nouveau en utilisant la même adresse e-mail. title: La suppression de votre compte devrait être votre dernier recours pending: - cancel: pour annuler votre demande de réinitialisation de compte. - cancel_error: La demande n'a pas pu être annulée. Veuillez réessayer sous peu. - delete_and_recreate: vous pourrez supprimer et recréer votre compte. + cancel_html: "%{click_here} pour annuler votre demande de réinitialisation de + compte." + click_here: Cliquez ici header: Vous avez demandé une réinitialisation de compte request: access_your_account: accéder à votre compte diff --git a/config/locales/dotiw/en.yml b/config/locales/dotiw/en.yml index 19a42c76d37..22d6966784a 100644 --- a/config/locales/dotiw/en.yml +++ b/config/locales/dotiw/en.yml @@ -2,4 +2,4 @@ en: datetime: dotiw: - two_words_connector: ' and ' + two_words_connector: " and" diff --git a/config/locales/dotiw/es.yml b/config/locales/dotiw/es.yml index b0a2e574dde..96366711e5b 100644 --- a/config/locales/dotiw/es.yml +++ b/config/locales/dotiw/es.yml @@ -2,4 +2,4 @@ es: datetime: dotiw: - two_words_connector: ' y ' + two_words_connector: " y" diff --git a/config/locales/dotiw/fr.yml b/config/locales/dotiw/fr.yml index 3892ac212e1..f7332edb792 100644 --- a/config/locales/dotiw/fr.yml +++ b/config/locales/dotiw/fr.yml @@ -2,4 +2,4 @@ fr: datetime: dotiw: - two_words_connector: ' et ' + two_words_connector: " et" diff --git a/config/locales/misc/en.yml b/config/locales/misc/en.yml deleted file mode 100644 index f57274a65b9..00000000000 --- a/config/locales/misc/en.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -en: - misc: - and: and - command: - click: Click - here: here - in: in diff --git a/config/locales/misc/es.yml b/config/locales/misc/es.yml deleted file mode 100644 index 88e013371b5..00000000000 --- a/config/locales/misc/es.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -es: - misc: - and: y - command: - click: Haga clic - here: aquí - in: en diff --git a/config/locales/misc/fr.yml b/config/locales/misc/fr.yml deleted file mode 100644 index 704404d7748..00000000000 --- a/config/locales/misc/fr.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -fr: - misc: - and: et - command: - click: Cliquez - here: ici - in: en diff --git a/config/locales/time/en.yml b/config/locales/time/en.yml index 3a723174c61..d5efd70acf9 100644 --- a/config/locales/time/en.yml +++ b/config/locales/time/en.yml @@ -2,22 +2,22 @@ en: date: month_names: - - - - January - - February - - March - - April - - May - - June - - July - - August - - September - - October - - November - - December + - + - January + - February + - March + - April + - May + - June + - July + - August + - September + - October + - November + - December time: am: AM formats: - event_timestamp: '%B %-d, %Y at %-l:%M %p' - event_timestamp_utc: '%B %-d, %Y at %-l:%M %p UTC' + event_timestamp: "%B %-d, %Y at %-l:%M %p" + event_timestamp_utc: "%B %-d, %Y at %-l:%M %p UTC" pm: PM diff --git a/config/locales/time/es.yml b/config/locales/time/es.yml index 5a1f19ba2de..4ed7e229f29 100644 --- a/config/locales/time/es.yml +++ b/config/locales/time/es.yml @@ -2,22 +2,22 @@ es: date: month_names: - - - - enero - - febrero - - marzo - - abril - - mayo - - junio - - julio - - agosto - - septiembre - - octubre - - noviembre - - diciembre + - + - enero + - febrero + - marzo + - abril + - mayo + - junio + - julio + - agosto + - septiembre + - octubre + - noviembre + - diciembre time: am: AM formats: - event_timestamp: '%e de %B de %Y a las %H:%M' - event_timestamp_utc: '%e de %B de %Y a las %H:%M UTC' + event_timestamp: "%e de %B de %Y a las %H:%M" + event_timestamp_utc: "%e de %B de %Y a las %H:%M UTC" pm: PM diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml index ac1a82789eb..bcdd20aa47a 100644 --- a/config/locales/time/fr.yml +++ b/config/locales/time/fr.yml @@ -2,22 +2,22 @@ fr: date: month_names: - - - - janvier - - février - - mars - - avril - - mai - - juin - - juillet - - août - - septembre - - octobre - - novembre - - décembre + - + - janvier + - février + - mars + - avril + - mai + - juin + - juillet + - août + - septembre + - octobre + - novembre + - décembre time: am: A.M. formats: - event_timestamp: '%e %B %Y à %H:%M' - event_timestamp_utc: '%e %B %Y à %H:%M UTC' + event_timestamp: "%e %B %Y à %H:%M" + event_timestamp_utc: "%e %B %Y à %H:%M UTC" pm: P.M. diff --git a/config/routes.rb b/config/routes.rb index ad494ba4e46..c92a85aaa27 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,7 +99,7 @@ delete '/account_reset/delete_account' => 'account_reset/delete_account#delete' get '/account_reset/confirm_delete_account' => 'account_reset/confirm_delete_account#show' get '/account_reset/pending' => 'account_reset/pending#show' - get '/account_reset/pending/cancel' => 'account_reset/pending#cancel' + post '/account_reset/pending/cancel' => 'account_reset/pending#cancel' get '/login/two_factor/options' => 'two_factor_authentication/options#index' post '/login/two_factor/options' => 'two_factor_authentication/options#create' From 3a7a3d98e25dfa52cdf4094196bd86d3e6d34c1a Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:38:12 -0400 Subject: [PATCH 17/27] fix missing space --- app/presenters/account_reset/pending_presenter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb index cf9de0dbadc..28f26fe82e5 100644 --- a/app/presenters/account_reset/pending_presenter.rb +++ b/app/presenters/account_reset/pending_presenter.rb @@ -1,7 +1,6 @@ module AccountReset class PendingPresenter include ActionView::Helpers::DateHelper - include ActionView::Helpers::TranslationHelper attr_reader :account_reset_request @@ -15,6 +14,7 @@ def time_remaining_until_granted account_reset_request.requested_at + Figaro.env.account_reset_wait_period_days.to_i.days, true, highest_measures: 2, + two_words_connector: I18n.t('datetime.dotiw.two_words_connector') + ' ', ) end end From 1e727a04c7485a0c3d0a3580d2d02eb7f52b8363 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:54:57 -0400 Subject: [PATCH 18/27] add a feature spec --- .../account_reset/pending_request_spec.rb | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 spec/features/account_reset/pending_request_spec.rb diff --git a/spec/features/account_reset/pending_request_spec.rb b/spec/features/account_reset/pending_request_spec.rb new file mode 100644 index 00000000000..2d326723807 --- /dev/null +++ b/spec/features/account_reset/pending_request_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' + +feature 'Pending account reset request sign in' do + it 'gives the option to cancel the request on sign in' do + allow(Figaro.env).to receive(:otp_delivery_blocklist_maxretry).and_return('999') + + user = create(:user, :signed_up) + sign_in_user(user) + click_link t('two_factor_authentication.login_options_link_text') + click_link t('two_factor_authentication.account_reset.link') + click_button t('account_reset.request.yes_continue') + + Capybara.reset_session! + + sign_in_user(user) + + expect(page).to have_content(t('account_reset.pending.header')) + + click_on t('account_reset.pending.click_here') + + expect(page).to have_current_path( + login_two_factor_path(otp_delivery_preference: :sms, reauthn: false), + ) + + # Signing in a after cancelling should not show a pending request and go string to MFA + Capybara.reset_session! + sign_in_user(user) + expect(page).to have_current_path( + login_two_factor_path(otp_delivery_preference: :sms, reauthn: false), + ) + end +end From bef8d7fe84b399ec454fa61a55be50df37c6804d Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 15:56:26 -0400 Subject: [PATCH 19/27] fix a syntax error --- spec/controllers/account_reset/pending_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb index f673018a963..172bc0cd109 100644 --- a/spec/controllers/account_reset/pending_controller_spec.rb +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe AccountReset::PendingController do - descirbe '#show' do + describe '#show' do it 'renders' context 'when the account reset request does not exist' do From cbda31dc3607470ba898326b9a6a97650d49341f Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 16:06:50 -0400 Subject: [PATCH 20/27] Add controller spec --- .../account_reset/pending_controller_spec.rb | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb index 172bc0cd109..1b3a2d1bcdb 100644 --- a/spec/controllers/account_reset/pending_controller_spec.rb +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -1,19 +1,38 @@ require 'rails_helper' describe AccountReset::PendingController do - describe '#show' do - it 'renders' + let(:user) { create(:user) } + + before do + sign_in user + end + describe '#show' do context 'when the account reset request does not exist' do - it 'renders a 404' + it 'renders a 404' do + get :show + + expect(response).to render_template('pages/page_not_found') + end end end describe '#cancel' do - it 'cancels the account reset request' + it 'cancels the account reset request' do + account_reset_request = AccountResetRequest.create(user: user, requested_at: 1.hour.ago) + + post :cancel + + expect(account_reset_request.reload.cancelled_at).to_not be_nil + expect(response).to redirect_to(user_two_factor_authentication_url) + end context 'when the account reset request does not exist' do - it 'renders a 404' + it 'renders a 404' do + post :cancel + + expect(response).to render_template('pages/page_not_found') + end end end end From 1c2afc7e949ed3f174480c54bd645dd840afff34 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Fri, 22 May 2020 16:25:17 -0400 Subject: [PATCH 21/27] fix i18n --- config/locales/account_reset/fr.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/account_reset/fr.yml b/config/locales/account_reset/fr.yml index a076cd2d668..25038574b1e 100644 --- a/config/locales/account_reset/fr.yml +++ b/config/locales/account_reset/fr.yml @@ -32,6 +32,8 @@ fr: cancel_html: "%{click_here} pour annuler votre demande de réinitialisation de compte." click_here: Cliquez ici + delete_and_recreate_html: En %{interval} vous pourrez supprimer + et recréer votre compte. header: Vous avez demandé une réinitialisation de compte request: access_your_account: accéder à votre compte From eb382b2ae47048101ea1f04b3a24a22fd0cda49e Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 29 May 2020 13:06:30 -0400 Subject: [PATCH 22/27] Default param, cleaner locales, fix typo/spec **Why**: code review --- .../account_reset/pending_presenter.rb | 6 +- .../account_reset/cancel_request_for_user.rb | 4 +- config/locales/dotiw/en.yml | 2 +- config/locales/dotiw/es.yml | 2 +- config/locales/dotiw/fr.yml | 2 +- .../account_reset/pending_request_spec.rb | 2 +- .../account_reset/pending_presenter_spec.rb | 59 +++++++++++-------- 7 files changed, 42 insertions(+), 35 deletions(-) diff --git a/app/presenters/account_reset/pending_presenter.rb b/app/presenters/account_reset/pending_presenter.rb index 28f26fe82e5..280268a6059 100644 --- a/app/presenters/account_reset/pending_presenter.rb +++ b/app/presenters/account_reset/pending_presenter.rb @@ -8,13 +8,13 @@ def initialize(account_reset_request) @account_reset_request = account_reset_request end - def time_remaining_until_granted + def time_remaining_until_granted(now: Time.zone.now) distance_of_time_in_words( - Time.zone.now, + now, account_reset_request.requested_at + Figaro.env.account_reset_wait_period_days.to_i.days, true, highest_measures: 2, - two_words_connector: I18n.t('datetime.dotiw.two_words_connector') + ' ', + two_words_connector: " #{I18n.t('datetime.dotiw.two_words_connector')} ", ) end end diff --git a/app/services/account_reset/cancel_request_for_user.rb b/app/services/account_reset/cancel_request_for_user.rb index 7f0fa117538..78a9b34145f 100644 --- a/app/services/account_reset/cancel_request_for_user.rb +++ b/app/services/account_reset/cancel_request_for_user.rb @@ -6,8 +6,8 @@ def initialize(user) @user = user end - def call - account_reset_request.update!(cancelled_at: Time.zone.now) + def call(now: Time.zone.now) + account_reset_request.update!(cancelled_at: now) NotifyUserOfRequestCancellation.new(user).call end diff --git a/config/locales/dotiw/en.yml b/config/locales/dotiw/en.yml index 22d6966784a..2f40418190c 100644 --- a/config/locales/dotiw/en.yml +++ b/config/locales/dotiw/en.yml @@ -2,4 +2,4 @@ en: datetime: dotiw: - two_words_connector: " and" + two_words_connector: "and" diff --git a/config/locales/dotiw/es.yml b/config/locales/dotiw/es.yml index 96366711e5b..04110498eab 100644 --- a/config/locales/dotiw/es.yml +++ b/config/locales/dotiw/es.yml @@ -2,4 +2,4 @@ es: datetime: dotiw: - two_words_connector: " y" + two_words_connector: "y" diff --git a/config/locales/dotiw/fr.yml b/config/locales/dotiw/fr.yml index f7332edb792..c3ef37b0f70 100644 --- a/config/locales/dotiw/fr.yml +++ b/config/locales/dotiw/fr.yml @@ -2,4 +2,4 @@ fr: datetime: dotiw: - two_words_connector: " et" + two_words_connector: "et" diff --git a/spec/features/account_reset/pending_request_spec.rb b/spec/features/account_reset/pending_request_spec.rb index 2d326723807..9d5fd864534 100644 --- a/spec/features/account_reset/pending_request_spec.rb +++ b/spec/features/account_reset/pending_request_spec.rb @@ -22,7 +22,7 @@ login_two_factor_path(otp_delivery_preference: :sms, reauthn: false), ) - # Signing in a after cancelling should not show a pending request and go string to MFA + # Signing in after cancelling should not show a pending request and go string to MFA Capybara.reset_session! sign_in_user(user) expect(page).to have_current_path( diff --git a/spec/presenters/account_reset/pending_presenter_spec.rb b/spec/presenters/account_reset/pending_presenter_spec.rb index 5e96b979d74..e2d3c94e7f4 100644 --- a/spec/presenters/account_reset/pending_presenter_spec.rb +++ b/spec/presenters/account_reset/pending_presenter_spec.rb @@ -1,13 +1,10 @@ require 'rails_helper' describe AccountReset::PendingPresenter do + # I18n.locale = :en + let(:user) { create(:user) } - let(:hours) { 21 } - let(:minutes) { 13 } - let(:seconds) { 40 } - let(:requested_at) do - 24.hours.ago + hours.hours + minutes.minutes + seconds.seconds - end + let(:requested_at) { 22.hours.ago } let(:account_reset_request) do AccountResetRequest.new( user: user, @@ -18,48 +15,58 @@ subject { described_class.new(account_reset_request) } describe '#account_reset_request' do - it 'returns account reset request' do + it 'returns the account reset request' do expect(subject.account_reset_request.requested_at).to eq(requested_at) end end describe '#time_remaining_until_granted' do - around(:each) do |example| - I18n.locale = :en - Timecop.freeze Time.zone.now do - example.run + before { I18n.locale = :en } + + context 'when the remaining time is greater than 1 hour' do + let(:requested_at) { 24.hours.ago - (2.hours + 21.minutes) } + + it 'returns its description in hours and minutes' do + expect(subject.time_remaining_until_granted).to eq '2 hours and 21 minutes' end end - context 'when requested at is greater than an hour' do - it 'returns the description in hours and minutes' do - expect(subject.time_remaining_until_granted).to eq '21 hours and 13 minutes' + context 'when the remaining time is less than 2 hours' do + let(:requested_at) { 24.hours.ago - (1.hours + 49.minutes) } + + it 'returns its description in 1 hour and minutes' do + expect(subject.time_remaining_until_granted).to eq '1 hour and 49 minutes' end end - context 'when requested at is greater than a minute' do - let(:hours) { 0 } + context 'when the remaining time is greater than 1 minute' do + let(:requested_at) { 24.hours.ago - (13.minutes + 40.seconds) } - it 'returns the description in minutes and seconds' do + it 'returns its description in minutes and seconds' do expect(subject.time_remaining_until_granted).to eq '13 minutes and 40 seconds' end end - context 'when requested at is less than a minute' do - let(:hours) { 0 } - let(:minutes) { 0 } + context 'when the remaining time is less than 2 minutes' do + let(:requested_at) { 24.hours.ago - (1.minute + 25.seconds) } + + it 'returns its description in 1 minute and seconds' do + expect(subject.time_remaining_until_granted).to eq '1 minute and 25 seconds' + end + end + + context 'when the remaining time is less than 1 minute' do + let(:requested_at) { 24.hours.ago - 40.seconds } - it 'returns the description in minutes and seconds' do + it 'returns its description in minutes and seconds' do expect(subject.time_remaining_until_granted).to eq '40 seconds' end end - context 'when the request is less than a second' do - let(:hours) { 0 } - let(:minutes) { 0 } - let(:seconds) { 0 } + context 'when the remaining time is less than 1 second' do + let(:requested_at) { 24.hours.ago - 0.5.seconds } - it 'returns the description in minutes and seconds' do + it 'returns less than 1 second' do expect(subject.time_remaining_until_granted).to eq 'less than 1 second' end end From 1575839471d27f5991e414490cf91129871c46b4 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 29 May 2020 13:47:44 -0400 Subject: [PATCH 23/27] Rubocop fixes **Why**: Rubocop --- app/services/account_reset/cancel.rb | 2 +- spec/presenters/account_reset/pending_presenter_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/account_reset/cancel.rb b/app/services/account_reset/cancel.rb index e63b1f3a53c..deff885d8a4 100644 --- a/app/services/account_reset/cancel.rb +++ b/app/services/account_reset/cancel.rb @@ -54,4 +54,4 @@ def extra_analytics_attributes ) end end -end \ No newline at end of file +end diff --git a/spec/presenters/account_reset/pending_presenter_spec.rb b/spec/presenters/account_reset/pending_presenter_spec.rb index e2d3c94e7f4..42049c242c4 100644 --- a/spec/presenters/account_reset/pending_presenter_spec.rb +++ b/spec/presenters/account_reset/pending_presenter_spec.rb @@ -32,7 +32,7 @@ end context 'when the remaining time is less than 2 hours' do - let(:requested_at) { 24.hours.ago - (1.hours + 49.minutes) } + let(:requested_at) { 24.hours.ago - (1.hour + 49.minutes) } it 'returns its description in 1 hour and minutes' do expect(subject.time_remaining_until_granted).to eq '1 hour and 49 minutes' From 79b52d7a158f06a046a898cda5d7953ffceadb6b Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Fri, 29 May 2020 14:10:50 -0400 Subject: [PATCH 24/27] Fix YAML **Why**: To keep files normalized --- config/locales/dotiw/en.yml | 2 +- config/locales/dotiw/es.yml | 2 +- config/locales/dotiw/fr.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/dotiw/en.yml b/config/locales/dotiw/en.yml index 2f40418190c..f5c277acf76 100644 --- a/config/locales/dotiw/en.yml +++ b/config/locales/dotiw/en.yml @@ -2,4 +2,4 @@ en: datetime: dotiw: - two_words_connector: "and" + two_words_connector: and diff --git a/config/locales/dotiw/es.yml b/config/locales/dotiw/es.yml index 04110498eab..0c407bd25c2 100644 --- a/config/locales/dotiw/es.yml +++ b/config/locales/dotiw/es.yml @@ -2,4 +2,4 @@ es: datetime: dotiw: - two_words_connector: "y" + two_words_connector: y diff --git a/config/locales/dotiw/fr.yml b/config/locales/dotiw/fr.yml index c3ef37b0f70..d435f590725 100644 --- a/config/locales/dotiw/fr.yml +++ b/config/locales/dotiw/fr.yml @@ -2,4 +2,4 @@ fr: datetime: dotiw: - two_words_connector: "et" + two_words_connector: et From 0c95703a5373645a123b3282ae56f61ddee7b17c Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 3 Jun 2020 15:31:48 -0400 Subject: [PATCH 25/27] Added cancel confirmation interstitial **Why**: To allow user to back out of cancellation --- app/assets/stylesheets/components/_btn.scss | 4 ++++ .../account_reset/pending_controller.rb | 3 ++- .../account_reset/pending/cancel.html.erb | 7 ++++++ .../account_reset/pending/confirm.html.erb | 14 ++++++++++++ app/views/account_reset/pending/show.html.erb | 22 ++++++------------- config/locales/account_reset/en.yml | 12 +++++----- config/locales/account_reset/es.yml | 14 +++++++----- config/locales/account_reset/fr.yml | 14 +++++++----- config/locales/links/en.yml | 1 + config/locales/links/es.yml | 1 + config/locales/links/fr.yml | 1 + config/routes.rb | 1 + .../account_reset/pending_request_spec.rb | 21 +++++++++--------- .../account_reset/pending_presenter_spec.rb | 2 -- .../cancel_request_for_user_spec.rb | 5 +++-- 15 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 app/views/account_reset/pending/cancel.html.erb create mode 100644 app/views/account_reset/pending/confirm.html.erb diff --git a/app/assets/stylesheets/components/_btn.scss b/app/assets/stylesheets/components/_btn.scss index 97c0a238a89..50d8ec6e011 100644 --- a/app/assets/stylesheets/components/_btn.scss +++ b/app/assets/stylesheets/components/_btn.scss @@ -6,6 +6,10 @@ .btn { white-space: normal; + + &.no-grow { + width: unset; + } } %btn-basic { diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 1dbc415ada3..99f28a68af1 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -10,10 +10,11 @@ def show @pending_presenter = AccountReset::PendingPresenter.new(pending_account_reset_request) end + def confirm; end + def cancel analytics.track_event(Analytics::PENDING_ACCOUNT_RESET_CANCELLED) AccountReset::CancelRequestForUser.new(current_user).call - redirect_to user_two_factor_authentication_url end private diff --git a/app/views/account_reset/pending/cancel.html.erb b/app/views/account_reset/pending/cancel.html.erb new file mode 100644 index 00000000000..9374c9343b1 --- /dev/null +++ b/app/views/account_reset/pending/cancel.html.erb @@ -0,0 +1,7 @@ +

<%= t('account_reset.pending.cancelled') %>

+ +<%= link_to( + t('links.continue_sign_in'), + user_two_factor_authentication_path, + class: 'btn usa-button usa-button--primary', +) %> diff --git a/app/views/account_reset/pending/confirm.html.erb b/app/views/account_reset/pending/confirm.html.erb new file mode 100644 index 00000000000..3c66e3b2e86 --- /dev/null +++ b/app/views/account_reset/pending/confirm.html.erb @@ -0,0 +1,14 @@ +

<%= t('account_reset.pending.confirm') %>

+ +<%= button_to( + t('forms.buttons.continue'), + account_reset_pending_cancel_path, + class: 'btn usa-button usa-button--primary margin-right-6 no-grow', + form_class: 'display-inline', +) %> + +<%= link_to( + t('links.go_back'), + account_reset_pending_path, + class: '', +) %> \ No newline at end of file diff --git a/app/views/account_reset/pending/show.html.erb b/app/views/account_reset/pending/show.html.erb index 5bc207a194c..a00be8394fa 100644 --- a/app/views/account_reset/pending/show.html.erb +++ b/app/views/account_reset/pending/show.html.erb @@ -1,22 +1,14 @@ -

- <%= t('account_reset.pending.header') %> -

+

<%= t('account_reset.pending.header') %>

<%= t( - 'account_reset.pending.delete_and_recreate_html', + 'account_reset.pending.wait_html', interval: @pending_presenter.time_remaining_until_granted, ) %>

-

- <%= t( - 'account_reset.pending.cancel_html', - click_here: button_to( - t('account_reset.pending.click_here'), - account_reset_pending_cancel_path, - class: 'btn btn-link', - form_class: 'display-inline', - ), - ) %> -

+<%= link_to( + t('account_reset.pending.cancel_request'), + account_reset_pending_confirm_path, + class: 'btn usa-button usa-button--primary', +) %> diff --git a/config/locales/account_reset/en.yml b/config/locales/account_reset/en.yml index 14c3406f8ea..75e24dacffd 100644 --- a/config/locales/account_reset/en.yml +++ b/config/locales/account_reset/en.yml @@ -28,11 +28,13 @@ en: the same email address. title: Deleting your account should be your last resort pending: - cancel_html: "%{click_here} to cancel your account reset request." - click_here: Click here - delete_and_recreate_html: In %{interval} you will be able to - delete and recreate your account. - header: You have requested an account reset + cancel_request: Cancel request + cancelled: We have cancelled your request to delete your account. + confirm: If you cancel now, you must create a new request and wait another 24 + hours to delete your account. + header: You requested to delete your account + wait_html: There is a 24-hour waiting period to delete your account. In %{interval}, + you will receive an email with instructions to complete the deletion. request: access_your_account: access your account are_you_sure: Are you sure you don't have access to any of your authentication diff --git a/config/locales/account_reset/es.yml b/config/locales/account_reset/es.yml index dab8b1128fb..b41c040db62 100644 --- a/config/locales/account_reset/es.yml +++ b/config/locales/account_reset/es.yml @@ -28,12 +28,14 @@ es: de correo electrónico. title: Eliminar tu cuenta debería ser tu último recurso pending: - cancel_html: "%{click_here} para cancelar su solicitud de restablecimiento de - cuenta." - click_here: Haga clic aquí - delete_and_recreate_html: En %{interval} podrá eliminar y volver - a crear su cuenta. - header: Has solicitado un restablecimiento de cuenta + cancel_request: Cancelar petición + cancelled: Hemos cancelado su solicitud para eliminar su cuenta. + confirm: Si cancela ahora, debe crear una nueva solicitud y esperar otras 24 + horas para eliminar su cuenta. + header: Solicitaste eliminar tu cuenta + wait_html: Hay un período de espera de 24 horas para eliminar su cuenta. En + %{interval}, recibirá un correo electrónico con instrucciones + para completar la eliminación. request: access_your_account: acceder a tu cuenta are_you_sure: "¿Estás seguro de que no tienes acceso a ninguno de tus métodos diff --git a/config/locales/account_reset/fr.yml b/config/locales/account_reset/fr.yml index 25038574b1e..ff3378f7aed 100644 --- a/config/locales/account_reset/fr.yml +++ b/config/locales/account_reset/fr.yml @@ -29,12 +29,14 @@ fr: nouveau en utilisant la même adresse e-mail. title: La suppression de votre compte devrait être votre dernier recours pending: - cancel_html: "%{click_here} pour annuler votre demande de réinitialisation de - compte." - click_here: Cliquez ici - delete_and_recreate_html: En %{interval} vous pourrez supprimer - et recréer votre compte. - header: Vous avez demandé une réinitialisation de compte + cancel_request: Demande d'annulation + cancelled: Nous avons annulé votre demande de suppression de votre compte. + confirm: Si vous annulez maintenant, vous devez créer une nouvelle demande et + attendre encore 24 heures pour supprimer votre compte. + header: Vous avez demandé de supprimer votre compte + wait_html: Il y a un délai d'attente de 24 heures pour supprimer votre compte. + Dans %{interval}, vous recevrez un e-mail avec des instructions + pour terminer la suppression. request: access_your_account: accéder à votre compte are_you_sure: Êtes-vous sûr de n'avoir accès à aucune de vos méthodes de sécurité? diff --git a/config/locales/links/en.yml b/config/locales/links/en.yml index 70248b042b7..9f886d9be9c 100644 --- a/config/locales/links/en.yml +++ b/config/locales/links/en.yml @@ -9,6 +9,7 @@ en: cancel: Cancel cancel_account_creation: "‹ Cancel account creation" contact: Contact + continue_sign_in: Continue sign in copy: Copy create_account: Create an account go_back: Go back diff --git a/config/locales/links/es.yml b/config/locales/links/es.yml index 02ac13fc811..596a7fbb1a2 100644 --- a/config/locales/links/es.yml +++ b/config/locales/links/es.yml @@ -9,6 +9,7 @@ es: cancel: Cancelar cancel_account_creation: "‹ Cancelar la creación de cuenta" contact: Contactar + continue_sign_in: Continuar el inicio de sesión copy: Copiar create_account: Crear cuenta go_back: Regresa diff --git a/config/locales/links/fr.yml b/config/locales/links/fr.yml index a97e989e583..e3fdf642352 100644 --- a/config/locales/links/fr.yml +++ b/config/locales/links/fr.yml @@ -9,6 +9,7 @@ fr: cancel: Annuler cancel_account_creation: "‹ Annuler la création du compte" contact: Contact + continue_sign_in: Continuer la connexion copy: Copier create_account: Créer un compte go_back: Retourner diff --git a/config/routes.rb b/config/routes.rb index c92a85aaa27..ba78ddce240 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,7 @@ delete '/account_reset/delete_account' => 'account_reset/delete_account#delete' get '/account_reset/confirm_delete_account' => 'account_reset/confirm_delete_account#show' get '/account_reset/pending' => 'account_reset/pending#show' + get '/account_reset/pending/confirm' => 'account_reset/pending#confirm' post '/account_reset/pending/cancel' => 'account_reset/pending#cancel' get '/login/two_factor/options' => 'two_factor_authentication/options#index' diff --git a/spec/features/account_reset/pending_request_spec.rb b/spec/features/account_reset/pending_request_spec.rb index 9d5fd864534..03297fc87df 100644 --- a/spec/features/account_reset/pending_request_spec.rb +++ b/spec/features/account_reset/pending_request_spec.rb @@ -13,20 +13,19 @@ Capybara.reset_session! sign_in_user(user) - expect(page).to have_content(t('account_reset.pending.header')) - click_on t('account_reset.pending.click_here') + click_on t('account_reset.pending.cancel_request') + expect(page).to have_current_path(account_reset_pending_confirm_path) - expect(page).to have_current_path( - login_two_factor_path(otp_delivery_preference: :sms, reauthn: false), - ) + click_on t('links.go_back') + expect(page).to have_content(t('account_reset.pending.header')) - # Signing in after cancelling should not show a pending request and go string to MFA - Capybara.reset_session! - sign_in_user(user) - expect(page).to have_current_path( - login_two_factor_path(otp_delivery_preference: :sms, reauthn: false), - ) + click_on t('account_reset.pending.cancel_request') + click_on t('forms.buttons.continue') + expect(page).to have_content(t('account_reset.pending.cancelled')) + + click_on t('links.continue_sign_in') + expect(page).to have_content(t('two_factor_authentication.header_text')) end end diff --git a/spec/presenters/account_reset/pending_presenter_spec.rb b/spec/presenters/account_reset/pending_presenter_spec.rb index 42049c242c4..779783468d4 100644 --- a/spec/presenters/account_reset/pending_presenter_spec.rb +++ b/spec/presenters/account_reset/pending_presenter_spec.rb @@ -1,8 +1,6 @@ require 'rails_helper' describe AccountReset::PendingPresenter do - # I18n.locale = :en - let(:user) { create(:user) } let(:requested_at) { 22.hours.ago } let(:account_reset_request) do diff --git a/spec/services/account_reset/cancel_request_for_user_spec.rb b/spec/services/account_reset/cancel_request_for_user_spec.rb index 9768fb6f6d1..7941f16ac31 100644 --- a/spec/services/account_reset/cancel_request_for_user_spec.rb +++ b/spec/services/account_reset/cancel_request_for_user_spec.rb @@ -7,10 +7,11 @@ subject { described_class.new(user) } describe '#call' do + let(:now) { Time.zone.now } it 'cancels the account reset request' do - subject.call + subject.call(now: now) - expect(account_reset_request.reload.cancelled_at).to be_within(1.second).of(Time.zone.now) + expect(account_reset_request.reload.cancelled_at).to eq(now) end it 'does not cancel account reset requests for a different user' do From 825100b69447a2b8e98cbe2c71cba745ed0535fd Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 3 Jun 2020 16:43:17 -0400 Subject: [PATCH 26/27] Fixed spec **Why**: controller method no longer redirects --- spec/controllers/account_reset/pending_controller_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb index 1b3a2d1bcdb..63e97171a32 100644 --- a/spec/controllers/account_reset/pending_controller_spec.rb +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -24,7 +24,6 @@ post :cancel expect(account_reset_request.reload.cancelled_at).to_not be_nil - expect(response).to redirect_to(user_two_factor_authentication_url) end context 'when the account reset request does not exist' do From 5dc68e58ef9552a593d23ac9a212d61a1307b747 Mon Sep 17 00:00:00 2001 From: "Shade L. Jenifer" Date: Wed, 3 Jun 2020 18:06:03 -0400 Subject: [PATCH 27/27] Fixed another spec **Why**: timesamp comparison was failing, likely due to missing to_i --- spec/services/account_reset/cancel_request_for_user_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/account_reset/cancel_request_for_user_spec.rb b/spec/services/account_reset/cancel_request_for_user_spec.rb index 7941f16ac31..09fc1a4f371 100644 --- a/spec/services/account_reset/cancel_request_for_user_spec.rb +++ b/spec/services/account_reset/cancel_request_for_user_spec.rb @@ -11,7 +11,7 @@ it 'cancels the account reset request' do subject.call(now: now) - expect(account_reset_request.reload.cancelled_at).to eq(now) + expect(account_reset_request.reload.cancelled_at.to_i).to eq(now.to_i) end it 'does not cancel account reset requests for a different user' do