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
26 changes: 18 additions & 8 deletions app/controllers/idv/gpo_verify_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Idv
class GpoVerifyController < ApplicationController
include IdvSession
include StepIndicatorConcern
include ThreatmetrixReviewConcern

before_action :confirm_two_factor_authenticated
before_action :confirm_verification_needed
Expand Down Expand Up @@ -43,14 +44,19 @@ def create
redirect_to idv_in_person_ready_to_verify_url
else
event, disavowal_token = create_user_event_with_disavowal(:account_verified)
UserAlerts::AlertUserAboutAccountVerified.call(
user: current_user,
date_time: event.created_at,
sp_name: decorated_session.sp_name,
disavowal_token: disavowal_token,
)
flash[:success] = t('account.index.verification.success')
redirect_to sign_up_completed_url

if result.extra[:threatmetrix_check_failed] && threatmetrix_enabled?
redirect_to_threatmetrix_review
else
UserAlerts::AlertUserAboutAccountVerified.call(
user: current_user,
date_time: event.created_at,
sp_name: decorated_session.sp_name,
disavowal_token: disavowal_token,
)
flash[:success] = t('account.index.verification.success')
redirect_to sign_up_completed_url
end
Comment thread
matthinz marked this conversation as resolved.
Outdated
end
else
flash[:error] = @gpo_verify_form.errors.first.message
Expand Down Expand Up @@ -93,5 +99,9 @@ def confirm_verification_needed
return if current_user.decorate.pending_profile_requires_verification?
redirect_to account_url
end

def threatmetrix_enabled?
IdentityConfig.store.proofing_device_profiling_decisioning_enabled
end
end
end
8 changes: 8 additions & 0 deletions app/forms/gpo_verify_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ def submit
if pending_in_person_enrollment?
UspsInPersonProofing::EnrollmentHelper.schedule_in_person_enrollment(user, pii)
pending_profile&.deactivate(:in_person_verification_pending)
elsif threatmetrix_check_failed?
pending_profile&.deactivate(:threatmetrix_review_pending)
else
activate_profile
end
Expand All @@ -34,6 +36,7 @@ def submit
enqueued_at: gpo_confirmation_code&.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
pending_in_person_enrollment: pending_in_person_enrollment?,
threatmetrix_check_failed: threatmetrix_check_failed?,
},
)
end
Expand Down Expand Up @@ -77,6 +80,11 @@ def pending_in_person_enrollment?
pending_profile&.proofing_components&.[]('document_check') == Idp::Constants::Vendors::USPS
end

def threatmetrix_check_failed?
status = pending_profile&.proofing_components&.[]('threatmetrix_review_status')
!status.nil? && status != 'pass'
end

def activate_profile
user.pending_profile&.activate
end
Expand Down
107 changes: 107 additions & 0 deletions spec/controllers/idv/gpo_verify_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
end
let(:proofing_components) { nil }
let(:user) { create(:user) }
let(:threatmetrix_enabled) { false }

before do
stub_analytics
Expand All @@ -28,6 +29,13 @@
)
allow(decorated_user).to receive(:pending_profile_requires_verification?).
and_return(has_pending_profile)

allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_enabled).
and_return(threatmetrix_enabled)
allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_required_to_verify).
and_return(threatmetrix_enabled)
allow(IdentityConfig.store).to receive(:proofing_device_profiling_decisioning_enabled).
and_return(threatmetrix_enabled)
end

describe '#index' do
Expand Down Expand Up @@ -109,6 +117,7 @@
success: true,
errors: {},
pending_in_person_enrollment: false,
threatmetrix_check_failed: false,
enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
)
Expand Down Expand Up @@ -146,6 +155,7 @@
success: true,
errors: {},
pending_in_person_enrollment: true,
threatmetrix_check_failed: false,
enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
)
Expand All @@ -163,6 +173,101 @@
action
end
end

context 'threatmetrix disabled' do
context 'with threatmetrix status of "reject"' do
let(:proofing_components) do
ProofingComponent.create(
user: user, threatmetrix: true,
threatmetrix_review_status: 'reject'
)
end

it 'redirects to the sign_up/completions page' do
expect(@analytics).to receive(:track_event).with(
'IdV: GPO verification submitted',
success: true,
errors: {},
pending_in_person_enrollment: false,
threatmetrix_check_failed: true,
enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
)
expect(@irs_attempts_api_tracker).to receive(:idv_gpo_verification_submitted).
with(success_properties)

action

disavowal_event_count = user.events.where(event_type: :account_verified, ip: '0.0.0.0').
where.not(disavowal_token_fingerprint: nil).count
expect(disavowal_event_count).to eq 1
expect(response).to redirect_to(sign_up_completed_url)
end
end
end

context 'threatmetrix enabled' do
let(:threatmetrix_enabled) { true }

context 'with threatmetrix status of "reject"' do
let(:proofing_components) do
ProofingComponent.create(
user: user, threatmetrix: true,
threatmetrix_review_status: 'reject'
)
end

it 'redirects to the sad face screen' do
expect(@analytics).to receive(:track_event).with(
'IdV: GPO verification submitted',
success: true,
errors: {},
pending_in_person_enrollment: false,
threatmetrix_check_failed: true,
enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
)

action

expect(response).to redirect_to(idv_setup_errors_url)
end

it 'does not show a flash message' do
expect(flash[:success]).to be_nil
action
end

it 'does not dispatch account verified alert' do
expect(UserAlerts::AlertUserAboutAccountVerified).not_to receive(:call)
action
end
end
Comment on lines 241 to 245
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


context 'with threatmetrix status of "review"' do
let(:proofing_components) do
ProofingComponent.create(
user: user, threatmetrix: true,
threatmetrix_review_status: 'review'
)
end
it 'redirects to the sad face screen' do
expect(@analytics).to receive(:track_event).with(
'IdV: GPO verification submitted',
success: true,
errors: {},
pending_in_person_enrollment: false,
threatmetrix_check_failed: true,
enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
)

action

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

context 'with an invalid form' do
Expand All @@ -174,6 +279,7 @@
success: false,
errors: otp_code_error_message,
pending_in_person_enrollment: false,
threatmetrix_check_failed: false,
enqueued_at: nil,
error_details: otp_code_incorrect,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
Expand Down Expand Up @@ -204,6 +310,7 @@
success: false,
errors: otp_code_error_message,
pending_in_person_enrollment: false,
threatmetrix_check_failed: false,
enqueued_at: nil,
error_details: otp_code_incorrect,
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
Expand Down
88 changes: 42 additions & 46 deletions spec/features/idv/steps/gpo_otp_verification_step_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
:profile,
deactivation_reason: :gpo_verification_pending,
pii: { ssn: '123-45-6789', dob: '1970-01-01' },
proofing_components: {
threatmetrix: threatmetrix_enabled,
threatmetrix_review_status: threatmetrix_review_status,
},
)
end
let(:gpo_confirmation_code) do
Expand All @@ -19,59 +23,51 @@
)
end
let(:user) { profile.user }

it 'prompts for one-time code at sign in' do
sign_in_live_with_2fa(user)

expect(current_path).to eq idv_gpo_verify_path
expect(page).to have_content t('idv.messages.gpo.resend')

gpo_confirmation_code
fill_in t('forms.verify_profile.name'), with: otp
click_button t('forms.verify_profile.submit')

expect(user.events.account_verified.size).to eq 1
expect(page).to_not have_content(t('account.index.verification.reactivate_button'))
end

it 'renders an error for an expired GPO OTP' do
sign_in_live_with_2fa(user)

gpo_confirmation_code.update(code_sent_at: 11.days.ago)
fill_in t('forms.verify_profile.name'), with: otp
click_button t('forms.verify_profile.submit')

expect(current_path).to eq idv_gpo_verify_path
expect(page).to have_content t('errors.messages.gpo_otp_expired')

user.reload

expect(user.events.account_verified.size).to eq 0
expect(user.active_profile).to be_nil
let(:threatmetrix_enabled) { false }
let(:threatmetrix_review_status) { nil }
let(:redirect_after_verification) { nil }
let(:profile_should_be_active) { true }
let(:expected_deactivation_reason) { nil }

before do
allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_enabled).
and_return(threatmetrix_enabled)
allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_required_to_verify).
and_return(threatmetrix_enabled)
allow(IdentityConfig.store).to receive(:proofing_device_profiling_decisioning_enabled).
and_return(threatmetrix_enabled)
end

it 'allows a user to resend a letter' do
allow(Base32::Crockford).to receive(:encode).and_return(otp)

sign_in_live_with_2fa(user)

expect(GpoConfirmation.count).to eq(0)
expect(GpoConfirmationCode.count).to eq(0)
click_on t('idv.messages.gpo.resend')
it_behaves_like 'gpo otp verification'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled these out into a shared example:

  • prompts for one-time code at sign in
  • renders an error for an expired GPO OTP
  • allows a user to resend a letter

The idea is that all the standard GPO OTP verification stuff should be unaffected by ThreatMetrix status: if you fail ThreatMetrix, you can still verify your account, but afterwards you'll have to take steps to fully activate.


expect_step_indicator_current_step(t('step_indicator.flows.idv.get_a_letter'))
context 'ThreatMetrix enabled' do
let(:threatmetrix_enabled) { true }

click_on t('idv.buttons.mail.resend')
context 'ThreatMetrix says "pass"' do
let(:threatmetrix_review_status) { 'pass' }
it_behaves_like 'gpo otp verification'
end

expect(GpoConfirmation.count).to eq(1)
expect(GpoConfirmationCode.count).to eq(1)
expect(current_path).to eq idv_come_back_later_path
context 'ThreatMetrix says "review"' do
let(:threatmetrix_review_status) { 'review' }
let(:redirect_after_verification) { idv_setup_errors_path }
let(:profile_should_be_active) { false }
let(:expected_deactivation_reason) { 'threatmetrix_review_pending' }
it_behaves_like 'gpo otp verification'
end

confirmation_code = GpoConfirmationCode.first
otp_fingerprint = Pii::Fingerprinter.fingerprint(otp)
context 'ThreatMetrix says "reject"' do
let(:threatmetrix_review_status) { 'reject' }
let(:redirect_after_verification) { idv_setup_errors_path }
let(:profile_should_be_active) { false }
let(:expected_deactivation_reason) { 'threatmetrix_review_pending' }
it_behaves_like 'gpo otp verification'
end

expect(confirmation_code.otp_fingerprint).to eq(otp_fingerprint)
expect(confirmation_code.profile).to eq(profile)
context 'No ThreatMetrix result on proofing component' do
let(:threatmetrix_review_status) { nil }
it_behaves_like 'gpo otp verification'
end
end

context 'with gpo feature disabled' do
Expand Down
30 changes: 30 additions & 0 deletions spec/forms/gpo_verify_form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,36 @@
expect(enrollment.enrollment_code).to be_a(String)
end
end

context 'ThreatMetrix rejection' do
let(:proofing_components) do
ProofingComponent.create(
user: user, threatmetrix: true,
threatmetrix_review_status: threatmetrix_review_status
)
end

let(:threatmetrix_review_status) { 'reject' }

before do
allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_enabled).
and_return(true)
allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_required_to_verify).
and_return(true)
allow(IdentityConfig.store).to receive(:proofing_device_profiling_decisioning_enabled).
and_return(true)
end

it 'returns true' do
result = subject.submit
expect(result.success?).to eq true
end

it 'notes that threatmetrix failed' do
result = subject.submit
expect(result.extra).to include(threatmetrix_check_failed: true)
end
end
end

context 'incorrect OTP' do
Expand Down
Loading