Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions app/controllers/idv/by_mail/resend_letter_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# frozen_string_literal: true

module Idv
module ByMail
class ResendLetterController < ApplicationController
include Idv::AvailabilityConcern
include IdvSessionConcern
include Idv::StepIndicatorConcern

before_action :confirm_two_factor_authenticated
before_action :confirm_verification_needed
before_action :confirm_mail_not_rate_limited
before_action :confirm_profile_not_too_old

def new
analytics.idv_resend_letter_visited
end

def create
update_tracking

if 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
end
end

def gpo_mail_service
@gpo_mail_service ||= Idv::GpoMail.new(current_user)
end

private

def confirm_verification_needed
return if current_user.gpo_verification_pending_profile?
redirect_to account_url
end

def confirm_profile_not_too_old
redirect_to idv_verify_by_mail_enter_code_path if gpo_mail_service.profile_too_old?
end

def confirm_mail_not_rate_limited
redirect_to idv_verify_by_mail_enter_code_path if gpo_mail_service.rate_limited?
end

def update_tracking
analytics.idv_gpo_address_letter_requested(
resend: true,
first_letter_requested_at: first_letter_requested_at,
hours_since_first_letter:
hours_since_first_letter(first_letter_requested_at),
phone_step_attempts: RateLimiter.new(
user: current_user,
rate_limit_type: :proof_address,
).attempts,
)
create_user_event(:gpo_mail_sent, current_user)
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 hours_since_first_letter(first_letter_requested_at)
first_letter_requested_at ?
(Time.zone.now - first_letter_requested_at).to_i.seconds.in_hours.to_i : 0
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:
hours_since_first_letter(first_letter_requested_at),
phone_step_attempts: RateLimiter.new(
user: current_user,
rate_limit_type: :proof_address,
).attempts,
)
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,
service_provider: current_sp,
profile: current_user.pending_profile,
)
confirmation_maker.perform
confirmation_maker
end

def pii
Pii::Cacher.new(current_user, user_session).
fetch(current_user.gpo_verification_pending_profile.id)
end

def send_reminder
current_user.send_email_to_all_addresses(:verify_by_mail_letter_requested)
end

def pii_locked?
!Pii::Cacher.new(current_user, user_session).exists_in_session?
end

def step_indicator_steps
if in_person_proofing?
Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS_GPO
else
StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO
end
end
end
end
end
11 changes: 11 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3275,6 +3275,17 @@ def idv_request_letter_visited(
)
end

# GPO "resend letter" page visited
# @identity.idp.previous_event_name IdV: request letter visited
def idv_resend_letter_visited(
**extra
)
track_event(
:idv_resend_letter_visited,
**extra,
)
end

# Acuant SDK errored after loading but before initialization
# @param [Boolean] success
# @param [String] error_message
Expand Down
35 changes: 35 additions & 0 deletions app/views/idv/by_mail/resend_letter/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<% self.title = t('titles.idv.get_letter') %>

<% content_for(:pre_flash_content) do %>
<%= render StepIndicatorComponent.new(
steps: step_indicator_steps,
current_step: :verify_address,
locale_scope: 'idv',
class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4',
) %>
<% end %>

<%= render PageHeadingComponent.new.with_content(t('idv.gpo.request_another_letter.title')) %>
<p>
<%= t('idv.gpo.request_another_letter.instructions_html') %>
</p>
<p>
<%= new_tab_link_to(
t('idv.gpo.request_another_letter.learn_more_link'),
help_center_redirect_url(
category: 'verify-your-identity',
article: 'verify-your-address-by-mail',
flow: :idv,
step: :gpo_send_letter,
),
) %>
</p>

<div class="margin-y-5">
<%= button_to t('idv.gpo.request_another_letter.button'),
idv_resend_letter_path,
method: 'put',
class: 'usa-button usa-button--big usa-button--wide' %>
</div>

<%= render 'idv/shared/back', fallback_path: idv_verify_by_mail_enter_code_url %>
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@
if FeatureManagement.gpo_verification_enabled?
get '/by_mail/request_letter' => 'by_mail/request_letter#index', as: :request_letter
put '/by_mail/request_letter' => 'by_mail/request_letter#create'
get '/by_mail/resend_letter' => 'by_mail/resend_letter#new', as: :resend_letter
put '/by_mail/resend_letter' => 'by_mail/resend_letter#create'
end

get '/by_mail/letter_enqueued' => 'by_mail/letter_enqueued#show', as: :letter_enqueued
Expand Down
119 changes: 119 additions & 0 deletions spec/controllers/idv/by_mail/resend_letter_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
require 'rails_helper'

RSpec.describe Idv::ByMail::ResendLetterController do
let(:user) { create(:user) }

before do
stub_sign_in(user)
stub_analytics
end

describe '#new' do
context 'the user has a gpo pending pending profile' do
before do
create(:profile, :verify_by_mail_pending, user: user)
end

it 'renders the confirmation page' do
get(:new)

expect(response).to have_http_status(200)
expect(@analytics).to have_logged_event(:idv_resend_letter_visited)
end
end

context 'the user does not have a gpo pending profile' do
it 'redirects to the account page' do
get(:new)

expect(response).to redirect_to(account_url)
end
end

context 'the user has a profile that is too old to request a new letter' do
before do
create(:profile, :verify_by_mail_pending, created_at: 100.days.ago, user: user)
end

it 'redirects to the enter OTP page' do
get(:new)

expect(response).to redirect_to(idv_verify_by_mail_enter_code_path)
end
end

context 'the user has sent to much mail' do
before do
profile = create(:profile, :verify_by_mail_pending, user: user)
create_list(:gpo_confirmation_code, 3, profile: profile)
end

it 'redirects to the enter OTP page' do
get(:new)

expect(response).to redirect_to(idv_verify_by_mail_enter_code_path)
end
end
end

describe '#new' do
before do
create(:profile, :verify_by_mail_pending, :with_pii, user: user)
end

it 'uses the GPO confirmation maker to send another letter and redirects', :freeze_time do
expect_to_resend_letter_and_redirect

expect(@analytics).to have_logged_event(
'IdV: USPS address letter requested',
hash_including(
resend: true,
first_letter_requested_at: user.pending_profile.gpo_verification_pending_at,
hours_since_first_letter: 24,
),
)

expect(@analytics).to have_logged_event(
'IdV: USPS address letter enqueued',
hash_including(
resend: true,
first_letter_requested_at: user.pending_profile.gpo_verification_pending_at,
hours_since_first_letter: 24,
enqueued_at: Time.zone.now,
proofing_components: nil,
),
)
end

it 'redirects to capture password controller if the PII is locked' do
pii_cacher = instance_double(Pii::Cacher)
allow(pii_cacher).to receive(:fetch).and_return(nil)
allow(pii_cacher).to receive(:exists_in_session?).and_return(false)
allow(Pii::Cacher).to receive(:new).and_return(pii_cacher)

put :create

expect(response).to redirect_to capture_password_path
end
end

def expect_to_resend_letter_and_redirect
pii = user.pending_profile.decrypt_pii(user.password).to_h
pii_cacher = instance_double(Pii::Cacher)
allow(pii_cacher).to receive(:fetch).with(user.pending_profile.id).and_return(pii)
allow(pii_cacher).to receive(:exists_in_session?).and_return(true)
allow(Pii::Cacher).to receive(:new).and_return(pii_cacher)

service_provider = create(:service_provider, issuer: '123abc')
session[:sp] = { issuer: service_provider.issuer, vtr: ['C1'] }

gpo_confirmation_maker = instance_double(GpoConfirmationMaker)
allow(GpoConfirmationMaker).to receive(:new).
with(pii: pii, service_provider: service_provider, profile: user.pending_profile).
and_return(gpo_confirmation_maker)

expect(gpo_confirmation_maker).to receive(:perform)
expect { put :create }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect(response).to redirect_to idv_letter_enqueued_path
end
end