diff --git a/app/controllers/idv/by_mail/enter_code_controller.rb b/app/controllers/idv/by_mail/enter_code_controller.rb index d7782d08a55..95376bf6c19 100644 --- a/app/controllers/idv/by_mail/enter_code_controller.rb +++ b/app/controllers/idv/by_mail/enter_code_controller.rb @@ -1,155 +1,157 @@ -module Idv::ByMail - class EnterCodeController < ApplicationController - include IdvSession - include Idv::StepIndicatorConcern - include FraudReviewConcern - - prepend_before_action :note_if_user_did_not_receive_letter - before_action :confirm_two_factor_authenticated - before_action :confirm_verification_needed - - def index - # GPO reminder emails include an "I did not receive my letter!" link that results in - # slightly different copy on this screen. - @user_did_not_receive_letter = !!params[:did_not_receive_letter] - - analytics.idv_verify_by_mail_enter_code_visited( - source: if @user_did_not_receive_letter then 'gpo_reminder_email' end, - ) - - if rate_limiter.limited? - render_rate_limited - return - end - - gpo_mail = Idv::GpoMail.new(current_user) - @gpo_verify_form = GpoVerifyForm.new(user: current_user, pii: pii) - @code = session[:last_gpo_confirmation_code] if FeatureManagement.reveal_gpo_code? - - @should_prompt_user_to_request_another_letter = - FeatureManagement.gpo_verification_enabled? && - !gpo_mail.mail_spammed? && - !gpo_mail.profile_too_old? +module Idv + module ByMail + class EnterCodeController < ApplicationController + include IdvSession + include Idv::StepIndicatorConcern + include FraudReviewConcern + + prepend_before_action :note_if_user_did_not_receive_letter + before_action :confirm_two_factor_authenticated + before_action :confirm_verification_needed + + def index + # GPO reminder emails include an "I did not receive my letter!" link that results in + # slightly different copy on this screen. + @user_did_not_receive_letter = !!params[:did_not_receive_letter] + + analytics.idv_verify_by_mail_enter_code_visited( + source: if @user_did_not_receive_letter then 'gpo_reminder_email' end, + ) - if pii_locked? - redirect_to capture_password_url - else - render :index + if rate_limiter.limited? + render_rate_limited + return + end + + gpo_mail = Idv::GpoMail.new(current_user) + @gpo_verify_form = GpoVerifyForm.new(user: current_user, pii: pii) + @code = session[:last_gpo_confirmation_code] if FeatureManagement.reveal_gpo_code? + + @should_prompt_user_to_request_another_letter = + FeatureManagement.gpo_verification_enabled? && + !gpo_mail.mail_spammed? && + !gpo_mail.profile_too_old? + + if pii_locked? + redirect_to capture_password_url + else + render :index + end end - end - def pii - Pii::Cacher.new(current_user, user_session).fetch - end - - def create - if rate_limiter.limited? - render_rate_limited - return + def pii + Pii::Cacher.new(current_user, user_session).fetch end - rate_limiter.increment! - @gpo_verify_form = build_gpo_verify_form + def create + if rate_limiter.limited? + render_rate_limited + return + end + rate_limiter.increment! - result = @gpo_verify_form.submit - analytics.idv_verify_by_mail_enter_code_submitted(**result.to_h) - irs_attempts_api_tracker.idv_gpo_verification_submitted( - success: result.success?, - failure_reason: irs_attempts_api_tracker.parse_failure_reason(result), - ) + @gpo_verify_form = build_gpo_verify_form - if !result.success? - flash[:error] = @gpo_verify_form.errors.first.message - redirect_to idv_verify_by_mail_enter_code_url - return - end + result = @gpo_verify_form.submit + analytics.idv_verify_by_mail_enter_code_submitted(**result.to_h) + irs_attempts_api_tracker.idv_gpo_verification_submitted( + success: result.success?, + failure_reason: irs_attempts_api_tracker.parse_failure_reason(result), + ) - prepare_for_personal_key + if !result.success? + flash[:error] = @gpo_verify_form.errors.first.message + redirect_to idv_verify_by_mail_enter_code_url + return + end - redirect_to idv_personal_key_url - end + prepare_for_personal_key - private + redirect_to idv_personal_key_url + end - def pending_in_person_enrollment? - return false unless IdentityConfig.store.in_person_proofing_enabled - current_user.pending_in_person_enrollment.present? - end + private - def account_not_ready_to_be_activated? - fraud_check_failed? || pending_in_person_enrollment? - end + def pending_in_person_enrollment? + return false unless IdentityConfig.store.in_person_proofing_enabled + current_user.pending_in_person_enrollment.present? + end - def note_if_user_did_not_receive_letter - if !current_user && params[:did_not_receive_letter] - # Stash that the user didn't receive their letter. - # Once the authentication process completes, they'll be redirected to complete their - # GPO verification... - session[:gpo_user_did_not_receive_letter] = true + def account_not_ready_to_be_activated? + fraud_check_failed? || pending_in_person_enrollment? end - if current_user && session.delete(:gpo_user_did_not_receive_letter) - # ...and we can pick things up here. - redirect_to idv_verify_by_mail_enter_code_path(did_not_receive_letter: 1) + def note_if_user_did_not_receive_letter + if !current_user && params[:did_not_receive_letter] + # Stash that the user didn't receive their letter. + # Once the authentication process completes, they'll be redirected to complete their + # GPO verification... + session[:gpo_user_did_not_receive_letter] = true + end + + if current_user && session.delete(:gpo_user_did_not_receive_letter) + # ...and we can pick things up here. + redirect_to idv_verify_by_mail_enter_code_path(did_not_receive_letter: 1) + end end - end - def prepare_for_personal_key - unless account_not_ready_to_be_activated? - event, _disavowal_token = create_user_event(:account_verified) + def prepare_for_personal_key + unless account_not_ready_to_be_activated? + event, _disavowal_token = create_user_event(:account_verified) + + UserAlerts::AlertUserAboutAccountVerified.call( + user: current_user, + date_time: event.created_at, + sp_name: decorated_session.sp_name, + ) + flash[:success] = t('account.index.verification.success') + end + + idv_session.address_verification_mechanism = 'gpo' + idv_session.address_confirmed! + end - UserAlerts::AlertUserAboutAccountVerified.call( + def rate_limiter + @rate_limiter ||= RateLimiter.new( user: current_user, - date_time: event.created_at, - sp_name: decorated_session.sp_name, + rate_limit_type: :verify_gpo_key, ) - flash[:success] = t('account.index.verification.success') end - idv_session.address_verification_mechanism = 'gpo' - idv_session.address_confirmed! - end - - def rate_limiter - @rate_limiter ||= RateLimiter.new( - user: current_user, - rate_limit_type: :verify_gpo_key, - ) - end - - def render_rate_limited - irs_attempts_api_tracker.idv_gpo_verification_rate_limited - analytics.rate_limit_reached( - limiter_type: :verify_gpo_key, - ) + def render_rate_limited + irs_attempts_api_tracker.idv_gpo_verification_rate_limited + analytics.rate_limit_reached( + limiter_type: :verify_gpo_key, + ) - @expires_at = rate_limiter.expires_at - render :rate_limited - end + @expires_at = rate_limiter.expires_at + render :rate_limited + end - def build_gpo_verify_form - GpoVerifyForm.new( - user: current_user, - pii: pii, - otp: params_otp, - ) - end + def build_gpo_verify_form + GpoVerifyForm.new( + user: current_user, + pii: pii, + otp: params_otp, + ) + end - def params_otp - params.require(:gpo_verify_form).permit(:otp)[:otp] - end + def params_otp + params.require(:gpo_verify_form).permit(:otp)[:otp] + end - def confirm_verification_needed - return if current_user.gpo_verification_pending_profile? - redirect_to account_url - end + def confirm_verification_needed + return if current_user.gpo_verification_pending_profile? + redirect_to account_url + end - def threatmetrix_enabled? - FeatureManagement.proofing_device_profiling_decisioning_enabled? - end + def threatmetrix_enabled? + FeatureManagement.proofing_device_profiling_decisioning_enabled? + end - def pii_locked? - !Pii::Cacher.new(current_user, user_session).exists_in_session? + def pii_locked? + !Pii::Cacher.new(current_user, user_session).exists_in_session? + end end end end diff --git a/app/controllers/idv/by_mail/letter_enqueued_controller.rb b/app/controllers/idv/by_mail/letter_enqueued_controller.rb index 2a9bf4fb720..44823646216 100644 --- a/app/controllers/idv/by_mail/letter_enqueued_controller.rb +++ b/app/controllers/idv/by_mail/letter_enqueued_controller.rb @@ -1,19 +1,21 @@ -module Idv::ByMail - class LetterEnqueuedController < ApplicationController - include IdvSession - include Idv::StepIndicatorConcern +module Idv + module ByMail + class LetterEnqueuedController < ApplicationController + include IdvSession + include Idv::StepIndicatorConcern - before_action :confirm_two_factor_authenticated - before_action :confirm_user_needs_gpo_confirmation + before_action :confirm_two_factor_authenticated + before_action :confirm_user_needs_gpo_confirmation - def show - analytics.idv_letter_enqueued_visit - end + def show + analytics.idv_letter_enqueued_visit + end - private + private - def confirm_user_needs_gpo_confirmation - redirect_to account_url unless current_user.gpo_verification_pending_profile? + def confirm_user_needs_gpo_confirmation + redirect_to account_url unless current_user.gpo_verification_pending_profile? + end end end end diff --git a/app/controllers/idv/by_mail/request_letter_controller.rb b/app/controllers/idv/by_mail/request_letter_controller.rb index 6871c080375..81d488a5332 100644 --- a/app/controllers/idv/by_mail/request_letter_controller.rb +++ b/app/controllers/idv/by_mail/request_letter_controller.rb @@ -1,130 +1,132 @@ -module Idv::ByMail - class RequestLetterController < ApplicationController - include IdvSession - include Idv::StepIndicatorConcern - include Idv::AbTestAnalyticsConcern - - before_action :confirm_two_factor_authenticated - before_action :confirm_idv_needed - before_action :confirm_user_completed_idv_profile_step - before_action :confirm_mail_not_spammed - before_action :confirm_profile_not_too_old - - def index - @presenter = RequestLetterPresenter.new(current_user, url_options) - @step_indicator_current_step = step_indicator_current_step - Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer). - call(:usps_address, :view, true) - analytics.idv_request_letter_visited( - letter_already_sent: @presenter.resend_requested?, - ) - end +module Idv + module ByMail + class RequestLetterController < ApplicationController + include IdvSession + include Idv::StepIndicatorConcern + include Idv::AbTestAnalyticsConcern + + before_action :confirm_two_factor_authenticated + before_action :confirm_idv_needed + before_action :confirm_user_completed_idv_profile_step + before_action :confirm_mail_not_spammed + before_action :confirm_profile_not_too_old + + def index + @presenter = RequestLetterPresenter.new(current_user, url_options) + @step_indicator_current_step = step_indicator_current_step + Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer). + call(:usps_address, :view, true) + analytics.idv_request_letter_visited( + letter_already_sent: @presenter.resend_requested?, + ) + end - def create - update_tracking - idv_session.address_verification_mechanism = :gpo - - if resend_requested? && pii_locked? - redirect_to capture_password_url - elsif resend_requested? - resend_letter - flash[:success] = t('idv.messages.gpo.another_letter_on_the_way') - redirect_to idv_letter_enqueued_url - else - redirect_to idv_review_url + def create + update_tracking + idv_session.address_verification_mechanism = :gpo + + if resend_requested? && pii_locked? + redirect_to capture_password_url + elsif resend_requested? + resend_letter + flash[:success] = t('idv.messages.gpo.another_letter_on_the_way') + redirect_to idv_letter_enqueued_url + else + redirect_to idv_review_url + end end - end - def gpo_mail_service - @gpo_mail_service ||= Idv::GpoMail.new(current_user) - end + def gpo_mail_service + @gpo_mail_service ||= Idv::GpoMail.new(current_user) + end - private + private - def confirm_profile_not_too_old - redirect_to idv_path if gpo_mail_service.profile_too_old? - end + def confirm_profile_not_too_old + redirect_to idv_path if gpo_mail_service.profile_too_old? + end - def step_indicator_current_step - if resend_requested? - :get_a_letter - else - :verify_phone_or_address + def step_indicator_current_step + if resend_requested? + :get_a_letter + else + :verify_phone_or_address + end end - end - def update_tracking - Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer). - call(:usps_letter_sent, :update, true) - - analytics.idv_gpo_address_letter_requested( - resend: resend_requested?, - first_letter_requested_at: first_letter_requested_at, - hours_since_first_letter: - gpo_mail_service.hours_since_first_letter(first_letter_requested_at), - phone_step_attempts: gpo_mail_service.phone_step_attempts, - **ab_test_analytics_buckets, - ) - irs_attempts_api_tracker.idv_gpo_letter_requested(resend: resend_requested?) - create_user_event(:gpo_mail_sent, current_user) - - ProofingComponent.find_or_create_by(user: current_user).update(address_check: 'gpo_letter') - end + def update_tracking + Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer). + call(:usps_letter_sent, :update, true) + + analytics.idv_gpo_address_letter_requested( + resend: resend_requested?, + first_letter_requested_at: first_letter_requested_at, + hours_since_first_letter: + gpo_mail_service.hours_since_first_letter(first_letter_requested_at), + phone_step_attempts: gpo_mail_service.phone_step_attempts, + **ab_test_analytics_buckets, + ) + irs_attempts_api_tracker.idv_gpo_letter_requested(resend: resend_requested?) + create_user_event(:gpo_mail_sent, current_user) + + ProofingComponent.find_or_create_by(user: current_user).update(address_check: 'gpo_letter') + end - def resend_requested? - current_user.gpo_verification_pending_profile? - end + def resend_requested? + current_user.gpo_verification_pending_profile? + end - def first_letter_requested_at - current_user.gpo_verification_pending_profile&.gpo_verification_pending_at - end + def first_letter_requested_at + current_user.gpo_verification_pending_profile&.gpo_verification_pending_at + end - def confirm_mail_not_spammed - redirect_to idv_review_url if idv_session.address_mechanism_chosen? && - gpo_mail_service.mail_spammed? - end + def confirm_mail_not_spammed + redirect_to idv_review_url if idv_session.address_mechanism_chosen? && + gpo_mail_service.mail_spammed? + end - def confirm_user_completed_idv_profile_step - # If the user has a pending profile, they may have completed idv in a - # different session and need a letter resent now - return if current_user.gpo_verification_pending_profile? - return if idv_session.verify_info_step_complete? + def confirm_user_completed_idv_profile_step + # If the user has a pending profile, they may have completed idv in a + # different session and need a letter resent now + return if current_user.gpo_verification_pending_profile? + return if idv_session.verify_info_step_complete? - redirect_to idv_verify_info_url - end + redirect_to idv_verify_info_url + end - def resend_letter - analytics.idv_gpo_address_letter_enqueued( - enqueued_at: Time.zone.now, - resend: true, - first_letter_requested_at: first_letter_requested_at, - hours_since_first_letter: - gpo_mail_service.hours_since_first_letter(first_letter_requested_at), - phone_step_attempts: gpo_mail_service.phone_step_attempts, - **ab_test_analytics_buckets, - ) - confirmation_maker = confirmation_maker_perform - send_reminder - return unless FeatureManagement.reveal_gpo_code? - session[:last_gpo_confirmation_code] = confirmation_maker.otp - end + def resend_letter + analytics.idv_gpo_address_letter_enqueued( + enqueued_at: Time.zone.now, + resend: true, + first_letter_requested_at: first_letter_requested_at, + hours_since_first_letter: + gpo_mail_service.hours_since_first_letter(first_letter_requested_at), + phone_step_attempts: gpo_mail_service.phone_step_attempts, + **ab_test_analytics_buckets, + ) + confirmation_maker = confirmation_maker_perform + send_reminder + return unless FeatureManagement.reveal_gpo_code? + session[:last_gpo_confirmation_code] = confirmation_maker.otp + end - def confirmation_maker_perform - confirmation_maker = GpoConfirmationMaker.new( - pii: Pii::Cacher.new(current_user, user_session).fetch, - service_provider: current_sp, - profile: current_user.pending_profile, - ) - confirmation_maker.perform - confirmation_maker - end + def confirmation_maker_perform + confirmation_maker = GpoConfirmationMaker.new( + pii: Pii::Cacher.new(current_user, user_session).fetch, + service_provider: current_sp, + profile: current_user.pending_profile, + ) + confirmation_maker.perform + confirmation_maker + end - def send_reminder - current_user.send_email_to_all_addresses(:letter_reminder) - end + def send_reminder + current_user.send_email_to_all_addresses(:letter_reminder) + end - def pii_locked? - !Pii::Cacher.new(current_user, user_session).exists_in_session? + def pii_locked? + !Pii::Cacher.new(current_user, user_session).exists_in_session? + end end end end diff --git a/app/presenters/idv/by_mail/request_letter_presenter.rb b/app/presenters/idv/by_mail/request_letter_presenter.rb index ee96ad76d7a..c95619606dd 100644 --- a/app/presenters/idv/by_mail/request_letter_presenter.rb +++ b/app/presenters/idv/by_mail/request_letter_presenter.rb @@ -1,51 +1,53 @@ -module Idv::ByMail - class RequestLetterPresenter - include Rails.application.routes.url_helpers +module Idv + module ByMail + class RequestLetterPresenter + include Rails.application.routes.url_helpers - attr_reader :current_user, :url_options + attr_reader :current_user, :url_options - def initialize(current_user, url_options) - @current_user = current_user - @url_options = url_options - end + def initialize(current_user, url_options) + @current_user = current_user + @url_options = url_options + end - def title - resend_requested? ? I18n.t('idv.titles.mail.resend') : I18n.t('idv.titles.mail.verify') - end + def title + resend_requested? ? I18n.t('idv.titles.mail.resend') : I18n.t('idv.titles.mail.verify') + end - def button - resend_requested? ? I18n.t('idv.buttons.mail.resend') : I18n.t('idv.buttons.mail.send') - end + def button + resend_requested? ? I18n.t('idv.buttons.mail.resend') : I18n.t('idv.buttons.mail.send') + end - def fallback_back_path - return idv_verify_info_path if OutageStatus.new.any_phone_vendor_outage? - user_needs_address_otp_verification? ? idv_verify_by_mail_enter_code_path : idv_phone_path - end + def fallback_back_path + return idv_verify_info_path if OutageStatus.new.any_phone_vendor_outage? + user_needs_address_otp_verification? ? idv_verify_by_mail_enter_code_path : idv_phone_path + end - def resend_requested? - current_user.gpo_verification_pending_profile? - end + def resend_requested? + current_user.gpo_verification_pending_profile? + end - def back_or_cancel_partial - if FeatureManagement.idv_by_mail_only? - 'idv/doc_auth/cancel' - else - 'idv/shared/back' + def back_or_cancel_partial + if FeatureManagement.idv_by_mail_only? + 'idv/doc_auth/cancel' + else + 'idv/shared/back' + end end - end - def back_or_cancel_parameters - if FeatureManagement.idv_by_mail_only? - { step: 'gpo' } - else - { fallback_path: fallback_back_path } + def back_or_cancel_parameters + if FeatureManagement.idv_by_mail_only? + { step: 'gpo' } + else + { fallback_path: fallback_back_path } + end end - end - private + private - def user_needs_address_otp_verification? - current_user.pending_profile? + def user_needs_address_otp_verification? + current_user.pending_profile? + end end end end diff --git a/config/routes.rb b/config/routes.rb index ad4ff4c4ed3..6b4fdebabde 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -388,28 +388,27 @@ get '/in_person/:step' => 'in_person#show', as: :in_person_step put '/in_person/:step' => 'in_person#update' - get '/by_mail' => 'by_mail/enter_code#index', as: :verify_by_mail_enter_code - post '/by_mail' => 'by_mail/enter_code#create' + get '/by_mail/enter_code' => 'by_mail/enter_code#index', as: :verify_by_mail_enter_code + post '/by_mail/enter_code' => 'by_mail/enter_code#create' get '/by_mail/confirm_start_over' => 'confirm_start_over#index', as: :confirm_start_over if FeatureManagement.gpo_verification_enabled? - get '/usps' => 'by_mail/request_letter#index', as: :request_letter - put '/usps' => 'by_mail/request_letter#create' - - # These will be made the new "official" routes in a future commit - get '/by_mail/request_letter' => 'by_mail/request_letter#index' + get '/by_mail/request_letter' => 'by_mail/request_letter#index', as: :request_letter put '/by_mail/request_letter' => 'by_mail/request_letter#create' + + # Temporary routes + redirects supporting GPO route renaming + get '/usps' => redirect('/verify/by_mail/request_letter') + put '/usps' => 'by_mail/request_letter#create' end - get '/come_back_later' => 'by_mail/letter_enqueued#show', as: :letter_enqueued + get '/by_mail/letter_enqueued' => 'by_mail/letter_enqueued#show', as: :letter_enqueued - # BEGIN temporary routes in preparation for renaming the GPO routes - # These will allow old instances to serve requests for new routes during the 50/50 - # state when new routes are deployed. - get '/by_mail/letter_enqueued' => 'by_mail/letter_enqueued#show' - get '/by_mail/enter_code' => 'by_mail/enter_code#index' - post '/by_mail/enter_code' => 'by_mail/enter_code#create' + # BEGIN temporary routes & redirects supporting GPO route renaming + get '/come_back_later' => redirect('/verify/by_mail/letter_enqueued') + + get '/by_mail' => redirect('/verify/by_mail/enter_code') + post '/by_mail' => 'by_mail/enter_code#create' # END temporary routes end diff --git a/spec/presenters/idv/by_mail/request_letter_presenter_spec.rb b/spec/presenters/idv/by_mail/request_letter_presenter_spec.rb index 6f2d9ce0cb4..547e54e3db6 100644 --- a/spec/presenters/idv/by_mail/request_letter_presenter_spec.rb +++ b/spec/presenters/idv/by_mail/request_letter_presenter_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' RSpec.describe Idv::ByMail::RequestLetterPresenter do + include Rails.application.routes.url_helpers + let(:user) { create(:user) } subject(:decorator) do @@ -67,13 +69,13 @@ context 'when the user has a pending profile' do it 'returns the verify account path' do create(:profile, user: user, gpo_verification_pending_at: 1.day.ago) - expect(subject.fallback_back_path).to eq('/verify/by_mail') + expect(subject.fallback_back_path).to eq(idv_verify_by_mail_enter_code_path) end end context 'when the user does not have a pending profile' do it 'returns the idv phone path' do - expect(subject.fallback_back_path).to eq('/verify/phone') + expect(subject.fallback_back_path).to eq(idv_phone_path) end end end