diff --git a/Gemfile b/Gemfile
index 6b671ec27e0..9cbb44f0a69 100644
--- a/Gemfile
+++ b/Gemfile
@@ -42,7 +42,7 @@ gem 'net-sftp'
gem 'newrelic_rpm', '~> 8.0'
gem 'pg'
gem 'phonelib'
-gem 'premailer-rails', '>= 1.11.1'
+gem 'premailer-rails', '>= 1.12.0'
gem 'profanity_filter'
gem 'rack', '>= 2.2.3.1'
gem 'rack-attack', '>= 6.2.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4e7644ded2b..ca2dafb71fd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -442,12 +442,13 @@ GEM
google-protobuf (>= 3.19.2)
phonelib (0.6.54)
pkcs11 (0.3.4)
- premailer (1.15.0)
+ premailer (1.21.0)
addressable
- css_parser (>= 1.6.0)
+ css_parser (>= 1.12.0)
htmlentities (>= 4.0.0)
- premailer-rails (1.11.1)
+ premailer-rails (1.12.0)
actionmailer (>= 3)
+ net-smtp
premailer (~> 1.7, >= 1.7.9)
profanity_filter (0.1.1)
pry (0.14.1)
@@ -778,7 +779,7 @@ DEPENDENCIES
pg
pg_query
phonelib
- premailer-rails (>= 1.11.1)
+ premailer-rails (>= 1.12.0)
profanity_filter
pry-byebug
pry-doc
diff --git a/app/controllers/concerns/fraud_review_concern.rb b/app/controllers/concerns/fraud_review_concern.rb
index cb59b4743bf..cdc93641f26 100644
--- a/app/controllers/concerns/fraud_review_concern.rb
+++ b/app/controllers/concerns/fraud_review_concern.rb
@@ -1,6 +1,11 @@
module FraudReviewConcern
extend ActiveSupport::Concern
+ delegate :fraud_check_failed?,
+ :fraud_review_pending?,
+ :fraud_rejection?,
+ to: :fraud_review_checker
+
def handle_fraud
handle_pending_fraud_review
handle_fraud_rejection
@@ -22,13 +27,7 @@ def redirect_to_fraud_rejection
redirect_to idv_not_verified_url
end
- def fraud_review_pending?
- return false unless user_fully_authenticated?
- current_user.fraud_review_pending?
- end
-
- def fraud_rejection?
- return false unless user_fully_authenticated?
- current_user.fraud_rejection?
+ def fraud_review_checker
+ @fraud_review_checker ||= FraudReviewChecker.new(current_user)
end
end
diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb
index 5e6b3c11ba1..6a791a76cb7 100644
--- a/app/controllers/concerns/idv/verify_info_concern.rb
+++ b/app/controllers/concerns/idv/verify_info_concern.rb
@@ -45,8 +45,7 @@ def update
user_id: current_user.id,
threatmetrix_session_id: flow_session[:threatmetrix_session_id],
request_ip: request.remote_ip,
- double_address_verification: current_user.establishing_in_person_enrollment&.
- capture_secondary_id_enabled || false,
+ double_address_verification: capture_secondary_id_enabled,
)
redirect_to after_update_url
@@ -54,6 +53,11 @@ def update
private
+ def capture_secondary_id_enabled
+ current_user.establishing_in_person_enrollment&.
+ capture_secondary_id_enabled || false
+ end
+
def should_use_aamva?(pii)
aamva_state?(pii) && !aamva_disallowed_for_service_provider?
end
diff --git a/app/controllers/idv/gpo_verify_controller.rb b/app/controllers/idv/gpo_verify_controller.rb
index 33a1608db9d..6d837c5a031 100644
--- a/app/controllers/idv/gpo_verify_controller.rb
+++ b/app/controllers/idv/gpo_verify_controller.rb
@@ -111,10 +111,6 @@ def confirm_verification_needed
redirect_to account_url
end
- def fraud_check_failed?
- threatmetrix_enabled? && (current_user.fraud_review_pending? || current_user.fraud_rejection?)
- end
-
def threatmetrix_enabled?
FeatureManagement.proofing_device_profiling_decisioning_enabled?
end
diff --git a/app/controllers/idv/in_person/verify_info_controller.rb b/app/controllers/idv/in_person/verify_info_controller.rb
index a9fd91a3ab0..154ad7c5641 100644
--- a/app/controllers/idv/in_person/verify_info_controller.rb
+++ b/app/controllers/idv/in_person/verify_info_controller.rb
@@ -13,6 +13,7 @@ class VerifyInfoController < ApplicationController
def show
@step_indicator_steps = step_indicator_steps
+ @capture_secondary_id_enabled = capture_secondary_id_enabled
analytics.idv_doc_auth_verify_visited(**analytics_arguments)
Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
diff --git a/app/controllers/idv/link_sent_controller.rb b/app/controllers/idv/link_sent_controller.rb
index dd5c5d4ccd5..2ef957d70c6 100644
--- a/app/controllers/idv/link_sent_controller.rb
+++ b/app/controllers/idv/link_sent_controller.rb
@@ -43,9 +43,13 @@ def extra_view_variables
private
def confirm_upload_step_complete
- return if flow_session['Idv::Steps::UploadStep']
+ return if flow_session[:flow_path] == 'hybrid'
- redirect_to idv_doc_auth_url
+ if flow_session[:flow_path] == 'standard'
+ redirect_to idv_document_capture_url
+ else
+ redirect_to idv_doc_auth_url
+ end
end
def confirm_document_capture_needed
diff --git a/app/controllers/idv/personal_key_controller.rb b/app/controllers/idv/personal_key_controller.rb
index 377b440de40..b622febb64d 100644
--- a/app/controllers/idv/personal_key_controller.rb
+++ b/app/controllers/idv/personal_key_controller.rb
@@ -3,6 +3,7 @@ class PersonalKeyController < ApplicationController
include IdvSession
include StepIndicatorConcern
include SecureHeadersConcern
+ include FraudReviewConcern
before_action :apply_secure_headers_override
before_action :confirm_two_factor_authenticated
@@ -23,8 +24,8 @@ def update
analytics.idv_personal_key_submitted(
address_verification_method: address_verification_method,
deactivation_reason: idv_session.profile&.deactivation_reason,
- fraud_review_pending: idv_session.profile&.fraud_review_pending?,
- fraud_rejection: idv_session.profile&.fraud_rejection?,
+ fraud_review_pending: fraud_review_pending?,
+ fraud_rejection: fraud_rejection?,
)
redirect_to next_step
end
@@ -38,7 +39,7 @@ def address_verification_method
def next_step
if in_person_enrollment?
idv_in_person_ready_to_verify_url
- elsif blocked_by_device_profiling?
+ elsif fraud_check_failed?
idv_please_call_url
elsif session[:sp]
sign_up_completed_url
@@ -91,10 +92,5 @@ def in_person_enrollment?
return false unless IdentityConfig.store.in_person_proofing_enabled
current_user.pending_in_person_enrollment.present?
end
-
- def blocked_by_device_profiling?
- !profile.active &&
- profile.fraud_review_pending? || profile.fraud_rejection?
- end
end
end
diff --git a/app/controllers/idv/review_controller.rb b/app/controllers/idv/review_controller.rb
index 9667c24058e..ba795fcb6d2 100644
--- a/app/controllers/idv/review_controller.rb
+++ b/app/controllers/idv/review_controller.rb
@@ -5,6 +5,7 @@ class ReviewController < ApplicationController
include IdvStepConcern
include StepIndicatorConcern
include PhoneConfirmation
+ include FraudReviewConcern
before_action :confirm_verify_info_step_complete
before_action :confirm_address_step_complete
@@ -18,8 +19,8 @@ def confirm_current_password
analytics.idv_review_complete(
success: false,
- fraud_review_pending: current_user.fraud_review_pending?,
- fraud_rejection: current_user.fraud_rejection?,
+ fraud_review_pending: fraud_review_pending?,
+ fraud_rejection: fraud_rejection?,
)
irs_attempts_api_tracker.idv_password_entered(success: false)
diff --git a/app/controllers/users/phones_controller.rb b/app/controllers/users/phones_controller.rb
index 1efdd37f611..1383d4d8b77 100644
--- a/app/controllers/users/phones_controller.rb
+++ b/app/controllers/users/phones_controller.rb
@@ -3,8 +3,9 @@ class PhonesController < ApplicationController
include PhoneConfirmation
include RecaptchaConcern
include ReauthenticationRequiredConcern
+ include MfaSetupConcern
- before_action :confirm_two_factor_authenticated
+ before_action :confirm_user_authenticated_for_2fa_setup
before_action :redirect_if_phone_vendor_outage
before_action :check_max_phone_numbers_per_account, only: %i[add create]
before_action :allow_csp_recaptcha_src, if: :recaptcha_enabled?
diff --git a/app/forms/gpo_verify_form.rb b/app/forms/gpo_verify_form.rb
index 18c8d5cd7a0..c8b180ad772 100644
--- a/app/forms/gpo_verify_form.rb
+++ b/app/forms/gpo_verify_form.rb
@@ -21,7 +21,7 @@ def submit
if pending_in_person_enrollment?
UspsInPersonProofing::EnrollmentHelper.schedule_in_person_enrollment(user, pii)
pending_profile&.deactivate(:in_person_verification_pending)
- elsif fraud_check_failed? && threatmetrix_enabled?
+ elsif fraud_review_checker.fraud_check_failed? && threatmetrix_enabled?
pending_profile&.remove_gpo_deactivation_reason
deactivate_for_fraud_review
else
@@ -37,7 +37,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: fraud_check_failed?,
+ threatmetrix_check_failed: fraud_review_checker.fraud_check_failed?,
},
)
end
@@ -89,8 +89,8 @@ def threatmetrix_enabled?
FeatureManagement.proofing_device_profiling_decisioning_enabled?
end
- def fraud_check_failed?
- user.fraud_review_pending? || user.fraud_rejection?
+ def fraud_review_checker
+ @fraud_review_checker ||= FraudReviewChecker.new(user)
end
def activate_profile
diff --git a/app/javascript/packages/document-capture/components/location-collection-item.scss b/app/javascript/packages/document-capture/components/location-collection-item.scss
index aefbc37d3e9..4b8605bbe2e 100644
--- a/app/javascript/packages/document-capture/components/location-collection-item.scss
+++ b/app/javascript/packages/document-capture/components/location-collection-item.scss
@@ -12,4 +12,9 @@
margin-top: 1rem;
padding-bottom: 1rem;
border-color: color('primary-light');
+
+ &:last-child {
+ border-bottom-style: none;
+ padding-bottom: 0;
+ }
}
diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb
index c7030ebae05..147dca8ebf2 100644
--- a/app/jobs/get_usps_proofing_results_job.rb
+++ b/app/jobs/get_usps_proofing_results_job.rb
@@ -370,7 +370,7 @@ def handle_successful_status_update(enrollment, response)
passed: true,
reason: 'Successful status update',
)
- enrollment.profile.activate
+ enrollment.profile.activate_after_passing_in_person
enrollment.update(
status: :passed,
proofed_at: proofed_at,
diff --git a/app/models/profile.rb b/app/models/profile.rb
index f9c698e45a3..b245e3a2e33 100644
--- a/app/models/profile.rb
+++ b/app/models/profile.rb
@@ -71,12 +71,19 @@ def activate_after_passing_review
activate
end
+ def activate_after_passing_in_person
+ update!(
+ deactivation_reason: nil,
+ )
+ activate
+ end
+
def deactivate(reason)
update!(active: false, deactivation_reason: reason)
end
def has_deactivation_reason?
- has_fraud_deactivation_reason? || gpo_verification_pending?
+ deactivation_reason.present? || has_fraud_deactivation_reason? || gpo_verification_pending?
end
def has_fraud_deactivation_reason?
diff --git a/app/models/user.rb b/app/models/user.rb
index 3fc8a3ef785..03bab176cfa 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -114,10 +114,6 @@ def gpo_verification_pending_profile
profiles.where.not(gpo_verification_pending_at: nil).order(created_at: :desc).first
end
- def fraud_review_eligible?
- fraud_review_pending_profile&.fraud_review_pending_at&.after?(30.days.ago)
- end
-
def fraud_review_pending?
fraud_review_pending_profile.present?
end
diff --git a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
index dabe125df9d..d7911e42a27 100644
--- a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
@@ -95,7 +95,7 @@ def cancel_link
def troubleshoot_change_phone_or_method_option
if unconfirmed_phone
{
- url: phone_setup_path,
+ url: add_phone_path,
text: t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
}
else
diff --git a/app/services/fraud_review_checker.rb b/app/services/fraud_review_checker.rb
new file mode 100644
index 00000000000..c9941aeb04b
--- /dev/null
+++ b/app/services/fraud_review_checker.rb
@@ -0,0 +1,23 @@
+class FraudReviewChecker
+ attr_reader :user
+
+ def initialize(user)
+ @user = user
+ end
+
+ def fraud_check_failed?
+ fraud_review_pending? || fraud_rejection?
+ end
+
+ def fraud_review_pending?
+ user&.fraud_review_pending_profile.present?
+ end
+
+ def fraud_rejection?
+ user&.fraud_rejection_profile.present?
+ end
+
+ def fraud_review_eligible?
+ !!user&.fraud_review_pending_profile&.fraud_review_pending_at&.after?(30.days.ago)
+ end
+end
diff --git a/app/services/idv/actions/redo_document_capture_action.rb b/app/services/idv/actions/redo_document_capture_action.rb
index ae0c395d529..62fb0f4d7a1 100644
--- a/app/services/idv/actions/redo_document_capture_action.rb
+++ b/app/services/idv/actions/redo_document_capture_action.rb
@@ -7,9 +7,14 @@ def self.analytics_submitted_event
def call
flow_session['redo_document_capture'] = true
- unless flow_session[:skip_upload_step]
- mark_step_incomplete(:link_sent)
+ if flow_session[:skip_upload_step]
+ redirect_to idv_document_capture_url
+ else
mark_step_incomplete(:upload)
+
+ if !IdentityConfig.store.doc_auth_link_sent_controller_enabled
+ mark_step_incomplete(:link_sent)
+ end
end
end
end
diff --git a/app/services/idv/steps/in_person/state_id_step.rb b/app/services/idv/steps/in_person/state_id_step.rb
index e99943eff03..e978f9d47fb 100644
--- a/app/services/idv/steps/in_person/state_id_step.rb
+++ b/app/services/idv/steps/in_person/state_id_step.rb
@@ -37,7 +37,7 @@ def call
end
end
- maybe_redirect_to_verify_info if updating_state_id?
+ maybe_redirect_to_verify_info(flow_session[steps[:address].to_s].blank?)
end
def extra_view_variables
diff --git a/app/services/idv/steps/temp_maybe_redirect_to_verify_info_helper.rb b/app/services/idv/steps/temp_maybe_redirect_to_verify_info_helper.rb
index b276c088348..7d4b4d8e319 100644
--- a/app/services/idv/steps/temp_maybe_redirect_to_verify_info_helper.rb
+++ b/app/services/idv/steps/temp_maybe_redirect_to_verify_info_helper.rb
@@ -1,14 +1,15 @@
+##
# This module and calls to it can be removed when the in_person_verify_info_controller_enabled
# flag is removed.
#
-
module Idv
module Steps
module TempMaybeRedirectToVerifyInfoHelper
private
- def maybe_redirect_to_verify_info
+ def maybe_redirect_to_verify_info(skip = false)
return unless IdentityConfig.store.in_person_verify_info_controller_enabled
+ return if skip
flow_session[:flow_path] = @flow.flow_path
redirect_to idv_in_person_verify_info_url
end
diff --git a/app/services/idv/steps/upload_step.rb b/app/services/idv/steps/upload_step.rb
index 009389bd78a..3b454710952 100644
--- a/app/services/idv/steps/upload_step.rb
+++ b/app/services/idv/steps/upload_step.rb
@@ -75,7 +75,8 @@ def handle_phone_submission
failure_reason: failure_reason,
)
- if IdentityConfig.store.doc_auth_link_sent_controller_enabled
+ if !failure_reason &&
+ IdentityConfig.store.doc_auth_link_sent_controller_enabled
flow_session[:flow_path] = 'hybrid'
redirect_to idv_link_sent_url
end
@@ -98,7 +99,7 @@ def application
def bypass_send_link_steps
mark_step_complete(:link_sent)
- flow_session[:flow_path] = @flow.flow_path
+ flow_session[:flow_path] = 'standard'
redirect_to idv_document_capture_url
form_response(destination: :document_capture)
diff --git a/app/views/idv/in_person/verify_info/show.html.erb b/app/views/idv/in_person/verify_info/show.html.erb
index fd5c71f4ebb..ef76d2e0c9d 100644
--- a/app/views/idv/in_person/verify_info/show.html.erb
+++ b/app/views/idv/in_person/verify_info/show.html.erb
@@ -40,8 +40,13 @@ locals:
<%= render PageHeadingComponent.new.with_content(t('headings.verify')) %>
-
+
+ <% if @capture_secondary_id_enabled %>
+
+ <%= t('headings.state_id') %>
+
+ <% end %>
- <%= t('idv.form.first_name') %>:
- <%= @pii[:first_name] %>
@@ -56,22 +61,55 @@ locals:
<%= I18n.l(Date.parse(@pii[:dob]), format: I18n.t('time.formats.event_date')) %>
+ <% if @capture_secondary_id_enabled %>
+
+
- <%= t('idv.form.issuing_state') %>:
+ - <%= @pii[:state_id_jurisdiction] %>
+
+ <% end %>
- <%= t('idv.form.id_number') %>:
- <%= @pii[:state_id_number] %>
+ <% if @capture_secondary_id_enabled %>
+
+
- <%= t('idv.form.address1') %>:
+ - <%= @pii[:identity_doc_address1] %>
+
+
+
- <%= t('idv.form.address2') %>:
+ - <%= @pii[:identity_doc_address2].presence %>
+
+
+
- <%= t('idv.form.city') %>:
+ - <%= @pii[:identity_doc_city] %>
+
+
+
- <%= t('idv.form.state') %>:
+ - <%= @pii[:identity_doc_address_state] %>
+
+
+
- <%= t('idv.form.zipcode') %>:
+ - <%= @pii[:identity_doc_zipcode] %>
+
+ <% end %>
<%= button_to(
idv_in_person_step_url(step: :redo_state_id),
method: :put,
- class: 'usa-button usa-button--unstyled',
+ class: 'usa-button usa-button--unstyled padding-y-1',
'aria-label': t('idv.buttons.change_state_id_label'),
) { t('idv.buttons.change_label') } %>
-
+
+ <% if @capture_secondary_id_enabled %>
+
+ <%= t('headings.residential_address') %>
+
+ <% end %>
- <%= t('idv.form.address1') %>:
- <%= @pii[:address1] %>
@@ -97,13 +135,18 @@ locals:
<%= button_to(
idv_in_person_step_url(step: :redo_address),
method: :put,
- class: 'usa-button usa-button--unstyled',
+ class: 'usa-button usa-button--unstyled padding-y-1',
'aria-label': t('idv.buttons.change_address_label'),
) { t('idv.buttons.change_label') } %>
+ <% if @capture_secondary_id_enabled %>
+
+ <%= t('headings.ssn') %>
+
+ <% end %>
<%= t('idv.form.ssn') %>:
<%= render(
'shared/masked_text',
@@ -121,7 +164,7 @@ locals:
<%= button_to(
idv_in_person_step_url(step: :redo_ssn),
method: :put,
- class: 'usa-button usa-button--unstyled',
+ class: 'usa-button usa-button--unstyled padding-y-1',
'aria-label': t('idv.buttons.change_ssn_label'),
) { t('idv.buttons.change_label') } %>
diff --git a/app/views/idv/link_sent/show.html.erb b/app/views/idv/link_sent/show.html.erb
index 98555371610..b4a18d20abc 100644
--- a/app/views/idv/link_sent/show.html.erb
+++ b/app/views/idv/link_sent/show.html.erb
@@ -57,4 +57,4 @@
<%= javascript_packs_tag_once 'doc-capture-polling' %>
<% end %>
-<%= render 'idv/shared/back', action: 'cancel_link_sent', class: 'link-sent-back-link', step_url: :idv_doc_auth_url %>
+<%= render 'idv/shared/back', action: 'cancel_link_sent', class: 'link-sent-back-link', step_url: :idv_doc_auth_step_url %>
diff --git a/app/views/idv/shared/_verify.html.erb b/app/views/idv/shared/_verify.html.erb
index 60b55456c20..9e8d59b4805 100644
--- a/app/views/idv/shared/_verify.html.erb
+++ b/app/views/idv/shared/_verify.html.erb
@@ -120,7 +120,7 @@ locals:
<% end %>
- <%= t('idv.form.ssn') %> :
+ <%= t('idv.form.ssn') %>:
<%= render(
'shared/masked_text',
text: SsnFormatter.format(pii[:ssn]),
diff --git a/lib/data_pull.rb b/lib/data_pull.rb
index 3ee39913917..4a09da6db8e 100644
--- a/lib/data_pull.rb
+++ b/lib/data_pull.rb
@@ -11,6 +11,7 @@ def initialize(argv:, stdout:, stderr:)
Result = Struct.new(
:table, # tabular output, rendered as an ASCII table or as CSV
+ :json, # output that should only be formatted as JSON
:subtask, # name of subtask, used for audit logging
:uuids, # Array of UUIDs entered or returned, used for audit logging
keyword_init: true,
@@ -20,6 +21,7 @@ def initialize(argv:, stdout:, stderr:)
:include_missing,
:format,
:show_help,
+ :requesting_issuers,
keyword_init: true,
) do
alias_method :include_missing?, :include_missing
@@ -31,6 +33,7 @@ def config
include_missing: true,
format: :table,
show_help: false,
+ requesting_issuers: [],
)
end
@@ -46,12 +49,16 @@ def run
return
end
- result = subtask_class.new.run(args: argv, include_missing: config.include_missing?)
+ result = subtask_class.new.run(args: argv, config:)
stderr.puts "*Task*: `#{result.subtask}`"
stderr.puts "*UUIDs*: #{result.uuids.map { |uuid| "`#{uuid}`" }.join(', ')}"
- render_output(result.table)
+ if result.json
+ stdout.puts result.json.to_json
+ else
+ render_output(result.table)
+ end
end
# @param [Array
>] rows
@@ -91,32 +98,44 @@ def render_output(rows)
# @api private
# A subtask is a class that has a run method, the type signature should look like:
- # +#run(args: Array, include_missing: Boolean) -> Result+
+ # +#run(args: Array, config: Config) -> Result+
# @return [Class,nil]
def subtask(name)
{
'uuid-lookup' => UuidLookup,
'uuid-convert' => UuidConvert,
'email-lookup' => EmailLookup,
+ 'ig-request' => InspectorGeneralRequest,
}[name]
end
+ # rubocop:disable Metrics/BlockLength
def option_parser
+ basename = File.basename($PROGRAM_NAME)
+
@option_parser ||= OptionParser.new do |opts|
opts.banner = <<~EOS
- #{$PROGRAM_NAME} [subcommand] [arguments] [options]
+ #{basename} [subcommand] [arguments] [options]
Example usage:
- * #{$PROGRAM_NAME} uuid-lookup email1@example.com email2@example.com
+ * #{basename} uuid-lookup email1@example.com email2@example.com
- * #{$PROGRAM_NAME} uuid-convert partner-uuid1 partner-uuid2
+ * #{basename} uuid-convert partner-uuid1 partner-uuid2
- * #{$PROGRAM_NAME} email-lookup uuid1 uuid2
+ * #{basename} email-lookup uuid1 uuid2
+
+ * #{basename} ig-request uuid1 uuid2 --requesting-issuer ABC:DEF:GHI
Options:
EOS
+ opts.on('-r=ISSUER', '--requesting-issuer=ISSUER', <<-MSG) do |issuer|
+ requesting issuer (used for ig-request task)
+ MSG
+ config.requesting_issuers << issuer
+ end
+
opts.on('--help') do
config.show_help = true
end
@@ -140,9 +159,10 @@ def option_parser
end
end
end
+ # rubocop:enable Metrics/BlockLength
class UuidLookup
- def run(args:, include_missing:)
+ def run(args:, config:)
emails = args
table = []
@@ -155,7 +175,7 @@ def run(args:, include_missing:)
if user
table << [email, user.uuid]
uuids << user.uuid
- elsif include_missing
+ elsif config.include_missing?
table << [email, '[NOT FOUND]']
end
end
@@ -169,7 +189,7 @@ def run(args:, include_missing:)
end
class UuidConvert
- def run(args:, include_missing:)
+ def run(args:, config:)
partner_uuids = args
table = []
@@ -180,7 +200,7 @@ def run(args:, include_missing:)
table << [identity.uuid, identity.agency.name, identity.user.uuid]
end
- if include_missing
+ if config.include_missing?
(partner_uuids - identities.map(&:uuid)).each do |missing_uuid|
table << [missing_uuid, '[NOT FOUND]', '[NOT FOUND]']
end
@@ -195,7 +215,7 @@ def run(args:, include_missing:)
end
class EmailLookup
- def run(args:, include_missing:)
+ def run(args:, config:)
uuids = args
users = User.includes(:email_addresses).where(uuid: uuids).order(:uuid)
@@ -209,7 +229,7 @@ def run(args:, include_missing:)
end
end
- if include_missing
+ if config.include_missing?
(uuids - users.map(&:uuid)).each do |missing_uuid|
table << [missing_uuid, '[NOT FOUND]', nil]
end
@@ -222,4 +242,25 @@ def run(args:, include_missing:)
)
end
end
+
+ class InspectorGeneralRequest
+ def run(args:, config:)
+ require 'data_requests/deployed'
+ ActiveRecord::Base.connection.execute('SET statement_timeout = 0')
+ uuids = args
+
+ users = uuids.map { |uuid| DataRequests::Deployed::LookupUserByUuid.new(uuid).call }.compact
+ shared_device_users = DataRequests::Deployed::LookupSharedDeviceUsers.new(users).call
+
+ output = shared_device_users.map do |user|
+ DataRequests::Deployed::CreateUserReport.new(user, config.requesting_issuers).call
+ end
+
+ Result.new(
+ subtask: 'ig-request',
+ uuids: users.map(&:uuid),
+ json: output,
+ )
+ end
+ end
end
diff --git a/lib/tasks/review_profile.rake b/lib/tasks/review_profile.rake
index a448b0917b3..9d888000c42 100644
--- a/lib/tasks/review_profile.rake
+++ b/lib/tasks/review_profile.rake
@@ -26,7 +26,7 @@ namespace :users do
next
end
- if user.fraud_review_eligible?
+ if FraudReviewChecker.new(user).fraud_review_eligible?
profile = user.fraud_review_pending_profile
profile.activate_after_passing_review
@@ -73,7 +73,7 @@ namespace :users do
next
end
- if user.fraud_review_eligible?
+ if FraudReviewChecker.new(user).fraud_review_eligible?
profile = user.fraud_review_pending_profile
profile.reject_for_fraud(notify_user: true)
diff --git a/spec/controllers/idv/link_sent_controller_spec.rb b/spec/controllers/idv/link_sent_controller_spec.rb
index 5e085dd1190..c2491123c6d 100644
--- a/spec/controllers/idv/link_sent_controller_spec.rb
+++ b/spec/controllers/idv/link_sent_controller_spec.rb
@@ -7,8 +7,7 @@
{ 'document_capture_session_uuid' => 'fd14e181-6fb1-4cdc-92e0-ef66dad0df4e',
:threatmetrix_session_id => 'c90ae7a5-6629-4e77-b97c-f1987c2df7d0',
:flow_path => 'hybrid',
- :phone_for_mobile_flow => '201-555-1212',
- 'Idv::Steps::UploadStep' => true }
+ :phone_for_mobile_flow => '201-555-1212' }
end
let(:user) { create(:user) }
@@ -79,13 +78,25 @@
)
end
- context 'upload step is not complete' do
- it 'redirects to idv_doc_auth_url' do
- flow_session['Idv::Steps::UploadStep'] = nil
+ context '#confirm_upload_step_complete' do
+ context 'no flow_path' do
+ it 'redirects to idv_doc_auth_url' do
+ flow_session[:flow_path] = nil
- get :show
+ get :show
+
+ expect(response).to redirect_to(idv_doc_auth_url)
+ end
+ end
+
+ context 'flow_path is standard' do
+ it 'redirects to idv_document_capture_url' do
+ flow_session[:flow_path] = 'standard'
+
+ get :show
- expect(response).to redirect_to(idv_doc_auth_url)
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
end
end
diff --git a/spec/decorators/service_provider_session_decorator_spec.rb b/spec/decorators/service_provider_session_decorator_spec.rb
index f608686978f..e8c360af671 100644
--- a/spec/decorators/service_provider_session_decorator_spec.rb
+++ b/spec/decorators/service_provider_session_decorator_spec.rb
@@ -173,7 +173,7 @@
service_provider_request: ServiceProviderRequestProxy.new,
)
- expect(subject.sp_logo_url).is_a? String
+ expect(subject.sp_logo_url).to be_kind_of(String)
end
end
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index df00aac3a8d..c93908a522b 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -227,7 +227,7 @@
end
end
- trait :deactivated_fraud_profile do
+ trait :fraud_review_pending do
fully_registered
after :build do |user|
@@ -241,6 +241,20 @@
end
end
+ trait :fraud_rejection do
+ fully_registered
+
+ after :build do |user|
+ create(
+ :profile,
+ :fraud_rejection,
+ :verified,
+ :with_pii,
+ user: user,
+ )
+ end
+ end
+
trait :deactivated_password_reset_profile do
fully_registered
diff --git a/spec/features/idv/actions/cancel_link_sent_action_spec.rb b/spec/features/idv/actions/cancel_link_sent_action_spec.rb
index 2cf54a9f177..e90d5e499e2 100644
--- a/spec/features/idv/actions/cancel_link_sent_action_spec.rb
+++ b/spec/features/idv/actions/cancel_link_sent_action_spec.rb
@@ -4,7 +4,11 @@
include IdvStepHelper
include DocAuthHelper
+ let(:new_controller_enabled) { false }
+
before do
+ allow(IdentityConfig.store).to receive(:doc_auth_link_sent_controller_enabled).
+ and_return(new_controller_enabled)
sign_in_and_2fa_user
complete_doc_auth_steps_before_link_sent_step
end
@@ -14,4 +18,15 @@
expect(page).to have_current_path(idv_doc_auth_upload_step)
end
+
+ context 'new SendLink controller is enabled' do
+ let(:new_controller_enabled) { true }
+
+ it 'returns to link sent step', :js do
+ expect(page).to have_current_path(idv_link_sent_path)
+ click_doc_auth_back_link
+
+ expect(page).to have_current_path(idv_doc_auth_upload_step)
+ end
+ end
end
diff --git a/spec/features/idv/actions/redo_document_capture_action_spec.rb b/spec/features/idv/actions/redo_document_capture_action_spec.rb
index bc45ad8e775..3e6db6cf5e9 100644
--- a/spec/features/idv/actions/redo_document_capture_action_spec.rb
+++ b/spec/features/idv/actions/redo_document_capture_action_spec.rb
@@ -4,8 +4,12 @@
include IdvStepHelper
include DocAuthHelper
+ let(:new_controller_enabled) { false }
+
context 'when barcode scan returns a warning', allow_browser_log: true do
before do
+ allow(IdentityConfig.store).to receive(:doc_auth_link_sent_controller_enabled).
+ and_return(new_controller_enabled)
sign_in_and_2fa_user
complete_doc_auth_steps_before_document_capture_step
mock_doc_auth_attention_with_barcode
@@ -80,5 +84,25 @@
expect(page).not_to have_css('[role="status"]')
end
end
+
+ context 'with doc_auth_link_sent_controller_enabled flag enabled',
+ driver: :headless_chrome_mobile do
+ let(:new_controller_enabled) { true }
+
+ it 'goes to document capture' do
+ warning_link_text = t('doc_auth.headings.capture_scan_warning_link')
+
+ expect(page).to have_css(
+ '[role="status"]',
+ text: t(
+ 'doc_auth.headings.capture_scan_warning_html',
+ link: warning_link_text,
+ ).tr(' ', ' '),
+ )
+ click_link warning_link_text
+
+ expect(current_path).to eq(idv_document_capture_path)
+ end
+ end
end
end
diff --git a/spec/features/idv/steps/in_person/verify_info_spec.rb b/spec/features/idv/steps/in_person/verify_info_spec.rb
index f7b9ecf03f9..a5a85a4dd22 100644
--- a/spec/features/idv/steps/in_person/verify_info_spec.rb
+++ b/spec/features/idv/steps/in_person/verify_info_spec.rb
@@ -135,4 +135,66 @@
# phone page
expect(page).to have_content(t('titles.idv.phone'))
end
+
+ context 'with in person verify info controller enabled ' do
+ let(:capture_secondary_id_enabled) { true }
+ let(:enrollment) { InPersonEnrollment.new(capture_secondary_id_enabled:) }
+ let(:user) { user_with_2fa }
+ let(:same_address_as_id) { true }
+ let(:double_address_verification) { true }
+
+ before do
+ allow(IdentityConfig.store).to receive(:in_person_capture_secondary_id_enabled).
+ and_return(true)
+ allow(IdentityConfig.store).to receive(:in_person_verify_info_controller_enabled).
+ and_return(true)
+ allow(user).to receive(:establishing_in_person_enrollment).
+ and_return(enrollment)
+ end
+
+ it 'displays expected headers & data on /verify_info',
+ allow_browser_log: true do
+ sign_in_and_2fa_user(user)
+ begin_in_person_proofing(user)
+ complete_prepare_step(user)
+ complete_location_step(user)
+ complete_state_id_step(
+ user, same_address_as_id: same_address_as_id,
+ double_address_verification: double_address_verification
+ )
+ click_idv_continue
+ complete_ssn_step(user)
+
+ # confirm url is /verify_info
+ expect(page).to have_current_path(idv_in_person_verify_info_path)
+
+ # verify page
+ expect(page).to have_content(t('headings.verify'))
+
+ # confirm headers are on template
+ expect(page).to have_content(t('headings.state_id').tr(' ', ' '))
+ expect(page).to have_content(t('headings.residential_address'))
+ expect(page).to have_content(t('headings.ssn'))
+
+ # confirm data is on template
+ expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME)
+ expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME)
+ expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT)
+ expect(page).to have_text(
+ "#{I18n.t('idv.form.issuing_state')}: #{Idp::Constants::
+ MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION}",
+ ).once
+ expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER)
+ expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice
+ expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice
+ expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice
+ expect(page).to have_text(
+ Idp::Constants::
+ MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:identity_doc_address_state],
+ ).twice
+
+ expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice
+ expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED)
+ end
+ end
end
diff --git a/spec/features/idv/threat_metrix_pending_spec.rb b/spec/features/idv/threat_metrix_pending_spec.rb
index 43198b5b55a..bafd3f8334a 100644
--- a/spec/features/idv/threat_metrix_pending_spec.rb
+++ b/spec/features/idv/threat_metrix_pending_spec.rb
@@ -69,6 +69,31 @@
expect(current_path).to eq('/auth/result')
end
+ scenario 'users rejected from fraud review cannot perform idv' do
+ user = create(:user, :fraud_rejection)
+
+ start_idv_from_sp
+ sign_in_live_with_2fa(user)
+
+ # User is redirected on IdV sign in
+ expect(page).to have_content(t('idv.failure.verify.heading'))
+ expect(page).to have_current_path(idv_not_verified_path)
+
+ visit idv_url
+
+ # User cannot enter IdV flow
+ expect(page).to have_content(t('idv.failure.verify.heading'))
+ expect(page).to have_current_path(idv_not_verified_path)
+
+ # User able to sign for IAL1
+ set_new_browser_session
+ visit_idp_from_sp_with_ial1(:oidc)
+ sign_in_live_with_2fa(user)
+ click_agree_and_continue
+
+ expect(current_path).to eq('/auth/result')
+ end
+
scenario 'users ThreatMetrix Pass, it logs idv_tmx_fraud_check event' do
freeze_time do
complete_all_idv_steps_with(threatmetrix: 'Pass')
diff --git a/spec/features/phone/add_phone_spec.rb b/spec/features/phone/add_phone_spec.rb
index e1943073afb..c68c3e12033 100644
--- a/spec/features/phone/add_phone_spec.rb
+++ b/spec/features/phone/add_phone_spec.rb
@@ -280,4 +280,20 @@
expect(user.reload.phone_configurations.count).to eq(0)
end
end
+
+ scenario 'troubleshoot adding a phone number' do
+ user = create(:user, :fully_registered)
+ phone = '+1 (225) 278-1234'
+
+ sign_in_and_2fa_user(user)
+ within('.sidenav') do
+ click_on t('account.navigation.add_phone_number')
+ end
+
+ fill_in :new_phone_form_phone, with: phone
+ click_continue
+ click_link t('two_factor_authentication.phone_verification.troubleshooting.change_number')
+
+ expect(page).to have_current_path(add_phone_path)
+ end
end
diff --git a/spec/lib/data_pull_spec.rb b/spec/lib/data_pull_spec.rb
index 4abec8ef71a..3700091f4a1 100644
--- a/spec/lib/data_pull_spec.rb
+++ b/spec/lib/data_pull_spec.rb
@@ -104,6 +104,29 @@
expect(JSON.parse(stdout.string)).to be_empty
end
end
+
+ describe 'ig-query task' do
+ let(:service_provider) { create(:service_provider) }
+ let(:identity) { IdentityLinker.new(user, service_provider).link_identity }
+
+ let(:argv) do
+ ['ig-request', identity.uuid, '--requesting-issuer', service_provider.issuer]
+ end
+
+ it 'runs the data requests report and prints it as JSON' do
+ data_pull.run
+
+ response = JSON.parse(stdout.string, symbolize_names: true)
+ expect(response.first.keys).to contain_exactly(
+ :user_id,
+ :login_uuid,
+ :requesting_issuer_uuid,
+ :email_addresses,
+ :mfa_configurations,
+ :user_events,
+ )
+ end
+ end
end
describe DataPull::UuidLookup do
@@ -114,8 +137,9 @@
let(:args) { [*users.map { |u| u.email_addresses.first.email }, 'missing@example.com'] }
let(:include_missing) { true }
+ let(:config) { DataPull::Config.new(include_missing:) }
- subject(:result) { subtask.run(args:, include_missing:) }
+ subject(:result) { subtask.run(args:, config:) }
it 'looks up the UUIDs for the given email addresses', aggregate_failures: true do
expect(result.table).to eq(
@@ -140,7 +164,8 @@
let(:args) { [*agency_identities.map(&:uuid), 'does-not-exist'] }
let(:include_missing) { true }
- subject(:result) { subtask.run(args:, include_missing:) }
+ let(:config) { DataPull::Config.new(include_missing:) }
+ subject(:result) { subtask.run(args:, config:) }
it 'converts the agency agency identities to internal UUIDs', aggregate_failures: true do
expect(result.table).to eq(
@@ -165,7 +190,8 @@
let(:args) { [user.uuid, 'does-not-exist'] }
let(:include_missing) { true }
- subject(:result) { subtask.run(args:, include_missing:) }
+ let(:config) { DataPull::Config.new(include_missing:) }
+ subject(:result) { subtask.run(args:, config:) }
it 'loads email addresses for the user', aggregate_failures: true do
expect(result.table).to match(
@@ -183,4 +209,33 @@
end
end
end
+
+ describe DataPull::InspectorGeneralRequest do
+ subject(:subtask) { DataPull::InspectorGeneralRequest.new }
+
+ describe '#run' do
+ let(:user) { create(:user) }
+ let(:service_provider) { create(:service_provider) }
+ let(:identity) { IdentityLinker.new(user, service_provider).link_identity }
+ let(:args) { [user.uuid] }
+ let(:config) { DataPull::Config.new(requesting_issuers: [service_provider.issuer]) }
+
+ subject(:result) { subtask.run(args:, config:) }
+
+ it 'runs the create users report, has a JSON-only response', aggregate_failures: true do
+ expect(result.table).to be_nil
+ expect(result.json.first.keys).to contain_exactly(
+ :user_id,
+ :login_uuid,
+ :requesting_issuer_uuid,
+ :email_addresses,
+ :mfa_configurations,
+ :user_events,
+ )
+
+ expect(result.subtask).to eq('ig-request')
+ expect(result.uuids).to eq([user.uuid])
+ end
+ end
+ end
end
diff --git a/spec/lib/tasks/review_profile_spec.rb b/spec/lib/tasks/review_profile_spec.rb
index 38a79addf9e..b38a99eb953 100644
--- a/spec/lib/tasks/review_profile_spec.rb
+++ b/spec/lib/tasks/review_profile_spec.rb
@@ -2,7 +2,7 @@
require 'rake'
describe 'review_profile' do
- let(:user) { create(:user, :deactivated_fraud_profile) }
+ let(:user) { create(:user, :fraud_review_pending) }
let(:uuid) { user.uuid }
let(:task_name) { nil }
@@ -53,6 +53,16 @@
expect(stdout.string).to include('Error: Could not find user with that UUID')
end
end
+
+ context 'when the user has cancelled verification' do
+ it 'does not activate the profile' do
+ user.profiles.first.update!(gpo_verification_pending_at: user.created_at)
+
+ invoke_task
+
+ expect(user.reload.profiles.first.active).to eq(false)
+ end
+ end
end
describe 'users:review:reject' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e74fe2bca5c..72d4730de15 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -529,47 +529,6 @@
end
end
- describe '#fraud_review_eligible?' do
- context 'when fraud_review_pending_at is nil' do
- it 'returns false' do
- fraud_review_pending_at = nil
-
- user = create(:user)
- user.profiles.create(
- fraud_review_pending_at: fraud_review_pending_at,
- )
-
- expect(user.fraud_review_eligible?).to be_falsey
- end
- end
-
- context 'when verified_at is within 30 days' do
- it 'returns true' do
- fraud_review_pending_at = 15.days.ago
-
- user = create(:user)
- user.profiles.create(
- fraud_review_pending_at: fraud_review_pending_at,
- )
-
- expect(user.fraud_review_eligible?).to eq true
- end
- end
-
- context 'when verified_at is older than 30 days' do
- it 'returns false' do
- fraud_review_pending_at = 45.days.ago
-
- user = create(:user)
- user.profiles.create(
- fraud_review_pending_at: fraud_review_pending_at,
- )
-
- expect(user.fraud_review_eligible?).to eq false
- end
- end
- end
-
describe '#fraud_review_pending?' do
it 'returns true if fraud review is pending' do
user = create(:user)
diff --git a/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb b/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
index 3d0b40289b2..1da1ffab4f9 100644
--- a/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
@@ -63,7 +63,7 @@
it 'should show an option to change phone number' do
expect(presenter.troubleshooting_options).to include(
{
- url: phone_setup_path,
+ url: add_phone_path,
text: t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
},
)
diff --git a/spec/requests/rack_attack_spec.rb b/spec/requests/rack_attack_spec.rb
index 3bf0ddf219b..e91280f2d1c 100644
--- a/spec/requests/rack_attack_spec.rb
+++ b/spec/requests/rack_attack_spec.rb
@@ -38,9 +38,18 @@
end
context 'when the request is for an asset' do
+ let(:asset_url) { '/assets/application.css' }
+ let(:asset_path) { Rails.root.join('public', asset_url.sub(/^\//, '')) }
+
+ before do
+ asset_dirname = File.dirname(asset_path)
+ FileUtils.mkdir_p(asset_dirname) unless File.directory?(asset_dirname)
+ File.write(asset_path, '') unless File.exist?(asset_path)
+ end
+
it 'does not throttle' do
(requests_per_ip_limit + 1).times do
- get '/assets/application.css', headers: { REMOTE_ADDR: '1.2.3.4' }
+ get asset_url, headers: { REMOTE_ADDR: '1.2.3.4' }
end
expect(response.status).to eq(200)
@@ -48,8 +57,16 @@
end
context 'when the request is for a pack' do
+ let(:pack_url) { '/packs/js/application.js' }
+ let(:pack_path) { Rails.root.join('public', pack_url.sub(/^\//, '')) }
+
+ before do
+ pack_dirname = File.dirname(pack_path)
+ FileUtils.mkdir_p(pack_dirname) unless File.directory?(pack_dirname)
+ File.write(pack_path, '') unless File.exist?(pack_path)
+ end
+
it 'does not throttle' do
- pack_url = Dir['public/packs/js/*'].first.gsub(/^public/, '')
(requests_per_ip_limit + 1).times do
get pack_url, headers: { REMOTE_ADDR: '1.2.3.4' }
end
diff --git a/spec/services/fraud_review_check_spec.rb b/spec/services/fraud_review_check_spec.rb
new file mode 100644
index 00000000000..07a6fde1f70
--- /dev/null
+++ b/spec/services/fraud_review_check_spec.rb
@@ -0,0 +1,95 @@
+require 'rails_helper'
+
+RSpec.describe FraudReviewChecker do
+ subject { described_class.new(user) }
+
+ describe '#fraud_check_failed?' do
+ context 'the user is not fraud review pending or rejected' do
+ let(:user) { create(:user) }
+
+ it { expect(subject.fraud_check_failed?).to eq(false) }
+ end
+
+ context 'the user is fraud review pending' do
+ let(:user) { create(:user, :fraud_review_pending) }
+
+ it { expect(subject.fraud_check_failed?).to eq(true) }
+ end
+
+ context 'the user is fraud review rejected' do
+ let(:user) { create(:user, :fraud_rejection) }
+
+ it { expect(subject.fraud_check_failed?).to eq(true) }
+ end
+ end
+
+ describe '#fraud_review_pending?' do
+ context 'the user is not fraud review pending or rejected' do
+ let(:user) { create(:user) }
+
+ it { expect(subject.fraud_review_pending?).to eq(false) }
+ end
+
+ context 'the user is fraud review pending' do
+ let(:user) { create(:user, :fraud_review_pending) }
+
+ it { expect(subject.fraud_review_pending?).to eq(true) }
+ end
+
+ context 'the user is fraud review rejected' do
+ let(:user) { create(:user, :fraud_rejection) }
+
+ it { expect(subject.fraud_review_pending?).to eq(false) }
+ end
+ end
+
+ describe '#fraud_rejection?' do
+ context 'the user is not fraud review pending or rejected' do
+ let(:user) { create(:user) }
+
+ it { expect(subject.fraud_rejection?).to eq(false) }
+ end
+
+ context 'the user is fraud review pending' do
+ let(:user) { create(:user, :fraud_review_pending) }
+
+ it { expect(subject.fraud_rejection?).to eq(false) }
+ end
+
+ context 'the user is fraud review rejected' do
+ let(:user) { create(:user, :fraud_rejection) }
+
+ it { expect(subject.fraud_rejection?).to eq(true) }
+ end
+ end
+
+ describe '#fraud_review_eligible?' do
+ context 'the user is not fraud review pending or rejected' do
+ let(:user) { create(:user) }
+
+ it { expect(subject.fraud_review_eligible?).to eq(false) }
+ end
+
+ context 'the user is fraud review pending for less than 30 days' do
+ let(:user) { create(:user, :fraud_review_pending) }
+
+ it { expect(subject.fraud_review_eligible?).to eq(true) }
+ end
+
+ context 'the user is fraud review pending for more than 30 days' do
+ let(:user) do
+ record = create(:user, :fraud_review_pending)
+ record.fraud_review_pending_profile.update!(fraud_review_pending_at: 31.days.ago)
+ record
+ end
+
+ it { expect(subject.fraud_review_eligible?).to eq(false) }
+ end
+
+ context 'the user is fraud review rejected' do
+ let(:user) { create(:user, :fraud_rejection) }
+
+ it { expect(subject.fraud_review_eligible?).to eq(false) }
+ end
+ end
+end
diff --git a/spec/support/sp_auth_helper.rb b/spec/support/sp_auth_helper.rb
index 20393659bfb..d5ebf28ed4d 100644
--- a/spec/support/sp_auth_helper.rb
+++ b/spec/support/sp_auth_helper.rb
@@ -57,7 +57,7 @@ def create_in_person_ial2_account_go_back_to_sp_and_sign_out(sp)
# Mark IPP as passed
enrollment = user.in_person_enrollments.last
expect(enrollment).to_not be_nil
- enrollment.profile.activate
+ enrollment.profile.activate_after_passing_in_person
enrollment.update(status: :passed)
visit_idp_from_sp_with_ial2(sp)
diff --git a/spec/views/shared/_nav_branded.html.erb_spec.rb b/spec/views/shared/_nav_branded.html.erb_spec.rb
index c1557ba0442..ff9f034c7d5 100644
--- a/spec/views/shared/_nav_branded.html.erb_spec.rb
+++ b/spec/views/shared/_nav_branded.html.erb_spec.rb
@@ -72,4 +72,21 @@
expect(rendered).to have_css("img[alt*='No logo no problem']")
end
end
+
+ context 'service provider has a poorly configured logo' do
+ before do
+ sp = build_stubbed(:service_provider, logo: 'abc')
+ decorated_session = ServiceProviderSessionDecorator.new(
+ sp:,
+ view_context:,
+ sp_session: {},
+ service_provider_request: nil,
+ )
+ allow(view).to receive(:decorated_session).and_return(decorated_session)
+ end
+
+ it 'does not raise an exception' do
+ expect { render }.not_to raise_exception
+ end
+ end
end
diff --git a/spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb b/spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb
index 512891e9c5c..8903b9c56b0 100644
--- a/spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb
+++ b/spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb
@@ -243,7 +243,7 @@
render
- expect(rendered).to have_link(t('forms.two_factor.try_again'), href: phone_setup_path)
+ expect(rendered).to have_link(t('forms.two_factor.try_again'), href: add_phone_path)
end
end
@@ -258,8 +258,7 @@
)
render
-
- expect(rendered).to have_link(t('forms.two_factor.try_again'), href: phone_setup_path)
+ expect(rendered).to have_link(t('forms.two_factor.try_again'), href: add_phone_path)
end
end
@@ -341,7 +340,7 @@
expect(rendered).to have_link(
t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
- href: phone_setup_path,
+ href: add_phone_path,
)
end
end
diff --git a/webpack.config.js b/webpack.config.js
index 63ff55a4c95..adaf513734c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -12,7 +12,7 @@ const isLocalhost = host === 'localhost';
const isProductionEnv = env === 'production';
const isTestEnv = env === 'test';
const mode = isProductionEnv ? 'production' : 'development';
-const hashSuffix = isProductionEnv ? '-[chunkhash:8]' : '';
+const hashSuffix = isProductionEnv ? '-[chunkhash:8].digested' : '';
const devServerPort = process.env.WEBPACK_PORT;
const devtool = process.env.WEBPACK_DEVTOOL || (isProductionEnv ? 'source-map' : 'eval-source-map');