From d78d5d9f66c95eb9bdf14b454f053002fe20d0c7 Mon Sep 17 00:00:00 2001 From: Matt Hinz Date: Mon, 3 Apr 2023 11:04:16 -0700 Subject: [PATCH 01/25] LG-9026: Phone finder failure screen updates (#8101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add current_step option to idv/shared/error Allow rendering an error screen with a step indicator * Update idv phone warning screen - EN only for now - Not wired in to controller, just view spec passing * Wire up PhoneErrorsController::warning Mit tësts! * Update feature specs * Split explanation + how long it takes * Add incomplete ES/FR translations Coupla keys missing, will follow up * Store international_code in session when trying phone # Want this for rendering purposes on the warning page. * Update attempts remaining copy Untranslated for now * Add missing translations * First draft of phone rate limited screen English only, tests not updated yet * Remove unneeded shared example There was only 1 use, so it's not really shared anymore? * Update "max attempts" shared examples * Remove unused i18n key * Add es/fr translations for rate limited screen * Update spec for phone_errors/failure.html.erb - More tests around GPO content - Fix ref to i18n key * Add more feature specs for phone rate limited screen * Fix Spanish translation left in French YAML * changelog: User-Facing Improvements, Identity verification, Improve error messaging around phone verification failure and highlight alternate paths. * Use ButtonComponent rather than button_or_link_to * button_or_link_to -> ButtonComponent * Simplify margin css classes --- app/controllers/idv/phone_controller.rb | 2 +- .../idv/phone_errors_controller.rb | 3 + app/services/idv/phone_step.rb | 5 +- app/views/idv/phone_errors/_warning.html.erb | 4 +- app/views/idv/phone_errors/failure.html.erb | 23 +++-- app/views/idv/phone_errors/warning.html.erb | 71 +++++++++++++- app/views/idv/shared/_error.html.erb | 14 +++ config/locales/idv/en.yml | 30 +++++- config/locales/idv/es.yml | 31 +++++- config/locales/idv/fr.yml | 31 +++++- .../idv/phone_errors_controller_spec.rb | 20 +++- spec/features/idv/steps/phone_step_spec.rb | 94 ++++++++++++++----- spec/support/idv_examples/max_attempts.rb | 21 ++--- .../idv/phone_errors/failure.html.erb_spec.rb | 33 ++++++- .../idv/phone_errors/warning.html.erb_spec.rb | 89 +++++++++++++++--- spec/views/idv/shared/_error.html.erb_spec.rb | 35 +++++++ 16 files changed, 424 insertions(+), 82 deletions(-) diff --git a/app/controllers/idv/phone_controller.rb b/app/controllers/idv/phone_controller.rb index 6afe9035396..b811b2ba8ae 100644 --- a/app/controllers/idv/phone_controller.rb +++ b/app/controllers/idv/phone_controller.rb @@ -126,7 +126,7 @@ def step end def step_params - params.require(:idv_phone_form).permit(:phone, :otp_delivery_preference) + params.require(:idv_phone_form).permit(:phone, :international_code, :otp_delivery_preference) end def confirm_step_needed diff --git a/app/controllers/idv/phone_errors_controller.rb b/app/controllers/idv/phone_errors_controller.rb index fed541d4730..0c2565b26c6 100644 --- a/app/controllers/idv/phone_errors_controller.rb +++ b/app/controllers/idv/phone_errors_controller.rb @@ -1,5 +1,6 @@ module Idv class PhoneErrorsController < ApplicationController + include StepIndicatorConcern include IdvSession before_action :confirm_two_factor_authenticated @@ -9,6 +10,8 @@ class PhoneErrorsController < ApplicationController def warning @remaining_attempts = throttle.remaining_count + @phone = idv_session.previous_phone_step_params[:phone] + @country_code = idv_session.previous_phone_step_params[:international_code] track_event(type: :warning) end diff --git a/app/services/idv/phone_step.rb b/app/services/idv/phone_step.rb index a8dea4dd8c9..2265d1c8d19 100644 --- a/app/services/idv/phone_step.rb +++ b/app/services/idv/phone_step.rb @@ -12,7 +12,10 @@ def submit(step_params) throttle.increment! self.step_params = step_params - idv_session.previous_phone_step_params = step_params.slice(:phone, :otp_delivery_preference) + idv_session.previous_phone_step_params = step_params.slice( + :phone, :international_code, + :otp_delivery_preference + ) proof_address end diff --git a/app/views/idv/phone_errors/_warning.html.erb b/app/views/idv/phone_errors/_warning.html.erb index 815aa1afcd5..13ff634a3c5 100644 --- a/app/views/idv/phone_errors/_warning.html.erb +++ b/app/views/idv/phone_errors/_warning.html.erb @@ -3,16 +3,18 @@ yields: Warning text to show. locals: * contact_support_option: Whether to show "Contact Support" option. Defaults to false. * name: Slug describing warning screen, for use in analytics. +* heading: Heading text, defaults to idv.failure.phone.heading %> <%= render( 'idv/shared/error', type: :warning, title: t('titles.failure.phone_verification'), - heading: t('idv.failure.phone.heading'), + heading: local_assigns.fetch(:heading, t('idv.failure.phone.heading')), action: { text: t('idv.failure.button.warning'), url: idv_phone_path, }, + current_step: :verify_phone_or_address, options: [ local_assigns[:contact_support_option] && { url: MarketingSite.contact_url, diff --git a/app/views/idv/phone_errors/failure.html.erb b/app/views/idv/phone_errors/failure.html.erb index adde1cf968d..43e8895946a 100644 --- a/app/views/idv/phone_errors/failure.html.erb +++ b/app/views/idv/phone_errors/failure.html.erb @@ -1,12 +1,9 @@ <%= render( 'idv/shared/error', title: t('titles.failure.phone_verification'), - heading: t('idv.failure.phone.heading'), + heading: t('idv.failure.phone.rate_limited.heading'), + current_step: :verify_phone_or_address, options: [ - @gpo_letter_available && { - text: t('idv.troubleshooting.options.verify_by_mail'), - url: idv_gpo_path, - }, decorated_session.sp_name && { url: return_to_sp_failure_to_proof_path(step: 'phone', location: request.params[:action]), text: t('idv.troubleshooting.options.get_help_at_sp', sp_name: decorated_session.sp_name), @@ -21,12 +18,24 @@ ) do %>

<%= t( - 'idv.failure.phone.fail_html', - timeout: distance_of_time_in_words( + 'idv.failure.phone.rate_limited.body', + time_left: distance_of_time_in_words( Time.zone.now, [@expires_at, Time.zone.now].compact.max, except: :seconds, ), ) %>

+ <% if @gpo_letter_available %> +

+ <%= t('idv.failure.phone.rate_limited.gpo.prompt') %> +

+
+ <%= render ButtonComponent.new( + action: ->(**tag_options, &block) { link_to idv_gpo_path, **tag_options, &block }, + big: true, + wide: true, + ).with_content(t('idv.failure.phone.rate_limited.gpo.button')) %> +
+ <% end %> <% end %> diff --git a/app/views/idv/phone_errors/warning.html.erb b/app/views/idv/phone_errors/warning.html.erb index 0add6f83ef4..54b57c45b5f 100644 --- a/app/views/idv/phone_errors/warning.html.erb +++ b/app/views/idv/phone_errors/warning.html.erb @@ -1,4 +1,67 @@ -<%= render('idv/phone_errors/warning') do %> -

<%= t('idv.failure.phone.warning') %>

-

<%= t('idv.failure.attempts', count: @remaining_attempts) %>

-<% end %> +<%= render( + 'idv/shared/error', + type: :warning, + title: t('titles.failure.phone_verification'), + heading: t('idv.failure.phone.warning.heading'), + current_step: :verify_phone_or_address, + options: [ + decorated_session.sp_name && { + url: return_to_sp_failure_to_proof_path( + step: 'phone', + location: local_assigns.fetch(:name, 'warning'), + ), + text: t('idv.troubleshooting.options.get_help_at_sp', sp_name: decorated_session.sp_name), + new_tab: true, + }, + ].select(&:present?), + ) do %> + +

+ <%= t('idv.failure.phone.warning.you_entered') %> + <%= PhoneFormatter.format(@phone, country_code: @country_code) %> +

+ +

+ <%= t('idv.failure.phone.warning.next_steps_html') %> + <%= new_window_link_to( + t('idv.failure.phone.warning.learn_more_link'), + help_center_redirect_path( + category: 'verify-your-identity', + article: 'phone-number-and-phone-plan-in-your-name', + flow: :idv, + step: :phone, + location: 'learn_more', + ), + ) %> +

+ +

+ <%= t('idv.failure.phone.warning.attempts', count: @remaining_attempts) %> +

+ +
+ <%= render ButtonComponent.new( + action: ->(**tag_options, &block) { link_to idv_phone_path, **tag_options, &block }, + big: true, + wide: true, + ).with_content(t('idv.failure.phone.warning.try_again_button')) %> +
+ + <% if @gpo_letter_available %> +
+ +

<%= t('idv.failure.phone.warning.gpo.heading') %>

+ +

+ <%= t('idv.failure.phone.warning.gpo.explanation') %> + <%= t('idv.failure.phone.warning.gpo.how_long_it_takes') %> +

+ +
+ <%= render ButtonComponent.new( + action: ->(**tag_options, &block) { link_to idv_gpo_path, **tag_options, &block }, + outline: true, + ).with_content(t('idv.failure.phone.warning.gpo.button')) %> +
+ <% end %> + <% end %> diff --git a/app/views/idv/shared/_error.html.erb b/app/views/idv/shared/_error.html.erb index b6b0cd71640..f9ca9393bb1 100644 --- a/app/views/idv/shared/_error.html.erb +++ b/app/views/idv/shared/_error.html.erb @@ -6,6 +6,7 @@ locals: * title: Optional custom page title, defaulting to the heading. * action: Optional hash of `text`, `url`, optional `method` of a primary action link. * action_secondary: Optional hash of `text`, `url`, optional `method` of a secondary action link. +* current_step: Optionally identify the current step of the IdV flow. If provided, the step indicator will be rendered. * options: Array of troubleshooting options. %> <% if local_assigns.fetch(:type, :error) == :error @@ -15,7 +16,20 @@ else icon_name = :warning troubleshooting_heading = t('components.troubleshooting_options.default_heading') end %> + <% title local_assigns.fetch(:title, heading) %> + +<% if local_assigns[:current_step] and defined?(step_indicator_steps) %> + <% content_for(:pre_flash_content) do %> + <%= render StepIndicatorComponent.new( + steps: step_indicator_steps, + current_step: local_assigns[:current_step], + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + ) %> + <% end %> +<% end %> + <%= render AlertIconComponent.new(icon_name: icon_name, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(heading) %> diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index 824e97c1e6e..827fed84495 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -76,14 +76,36 @@ en: back later. text_html: Please try again. If you keep getting these errors, %{link}. phone: - fail_html: 'Please try again in %{timeout}. For your security, - we limit the number of times you can attempt to verify your phone - number online.' heading: We could not match this phone number to other records jobfail: Something went wrong and we cannot process your request at this time. Please try again. + rate_limited: + body: For security reasons, we limit the number of times you can attempt to + verify your phone number online. You will need to wait %{time_left} + and start over before trying to verify by phone again. + gpo: + button: Verify by mail + prompt: Continue now by verifying by mail, which typically takes 3 - 7 business + days. + heading: 'We couldn’t verify your identity by phone' timeout: Our request to verify your information timed out. Please try again. - warning: Please check the information you entered and try again. + warning: + attempts: + one: For security reasons, you have one attempt remaining. + other: For security reasons, you have %{count} attempts remaining. + gpo: + button: Verify by mail + explanation: If you don’t have another phone number to try, verify by mail + instead. + heading: Verify by mail + how_long_it_takes: This typically takes 3 - 7 business days. + heading: We couldn’t match you to this number + learn_more_link: 'Learn more about what phone number to use' + next_steps_html: 'Please try again with another number that you + use often and have used for a long time. This can be a work or home + number.' + try_again_button: 'Try another number' + you_entered: 'You entered:' sessions: exception: There was an internal error processing your request. fail_html: 'Please try again in %{timeout}. For your security, diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index b4852c5381e..c93d8cbf53e 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -81,16 +81,39 @@ es: continúa, regrese más tarde. text_html: Inténtalo de nuevo. Si sigues recibiendo estos errores, %{link}. phone: - fail_html: 'Por favor, inténtelo de nuevo en %{timeout}. Por su - seguridad, limitamos el número de veces que puede intentar verificar - su número de teléfono en línea.' heading: No pudimos encontrar coincidencias entre este número de teléfono y otros registros jobfail: Algo ha fallado y no podemos procesar tu solicitud en este momento. Vuelve a intentarlo. + rate_limited: + body: Por motivos de seguridad, se limita el número de veces que puede intentar + verificar su número de teléfono por Internet. Tendrás que esperar + %{time_left} y volver a empezar antes de volver a intentar la + verificación por teléfono. + gpo: + button: Verificar por correo + prompt: Continúa ahora con la verificación por correo, que suele tardar entre 3 + y 7 días laborables. + heading: 'No hemos podido verificar su identidad por teléfono' timeout: Nuestra solicitud para verificar tu información ha caducado. Vuelve a intentarlo. - warning: Por favor, verifique la información que ingresó y vuelva a intentarlo. + warning: + attempts: + one: Por razones de seguridad, dispone de un solo intento. + other: Por razones de seguridad, le quedan %{count} intentos. + gpo: + button: Verificar por correo + explanation: Si no dispones de otro número de teléfono para intentarlo, + verifícalo por correo. + heading: Verificar por correo + how_long_it_takes: Suele tardar entre 3 y 7 días laborables. + heading: No pudimos asociarlo a este número + learn_more_link: 'Más información sobre qué número de teléfono usar' + next_steps_html: 'Vuelva a intentarlo con otro número que + utilice a menudo y desde hace tiempo. Puede ser un número del + trabajo o particular.' + try_again_button: 'Intente con otro número' + you_entered: 'Ud. entregó:' sessions: exception: Hubo un error interno al procesar su solicitud. fail_html: 'Por favor, inténtelo de nuevo en %{timeout}. Por su diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml index 87b78d0702e..19f128afde7 100644 --- a/config/locales/idv/fr.yml +++ b/config/locales/idv/fr.yml @@ -85,16 +85,39 @@ fr: problème persiste, revenez plus tard. text_html: Veuillez réessayer. Si vous continuez à recevoir ces erreurs, %{link} phone: - fail_html: 'Veuillez réessayer dans %{timeout}. Pour votre - sécurité, nous limitons le nombre de fois où vous pouvez tenter de - vérifier votre numéro de téléphone en ligne.' heading: Nous n’avons pas pu faire correspondre ce numéro de téléphone à d’autres enregistrements jobfail: Un problème s’est produit et nous ne pouvons pas traiter votre demande pour le moment. Veuillez réessayer. + rate_limited: + body: Pour des raisons de sécurité, nous limitons le nombre de tentatives de + vérification de votre numéro de téléphone en ligne. Vous devrez + attendre %{time_left} et recommencer avant d’essayer à nouveau de + vérifier votre identité par téléphone. + gpo: + button: Vérifier par courrier + prompt: Continuez maintenant en vérifiant par courrier, ce qui prend + généralement de 3 à 7 jours ouvrables. + heading: Nous n’avons pas pu vérifier votre identité par téléphone timeout: Notre demande de vérification de vos renseignements a expiré. Veuillez réessayer. - warning: Veuillez vérifier les informations que vous avez saisies et réessayer. + warning: + attempts: + one: Pour des raisons de sécurité, il vous reste une tentative. + other: Pour des raisons de sécurité, il vous reste %{count} tentatives. + gpo: + button: Vérifier par courrier + explanation: Si vous n’avez pas d’autre numéro de téléphone à essayer, vérifiez + plutôt par courrier. + heading: Vérifier par courrier + how_long_it_takes: Cette procédure prend typiquement 3 à 7 jours ouvrables. + heading: Nous n’avons pas pu vous associer à ce numéro + learn_more_link: 'Apprenez-en plus sur quel numéro de téléphone utiliser' + next_steps_html: 'Veuillez réessayer avec un autre numéro que + vous utilisez souvent et depuis longtemps. Il peut s’agir d’un + numéro professionnel ou personnel.' + try_again_button: 'Essayez un autre numéro' + you_entered: 'Tu as soumis:' sessions: exception: Une erreur interne s’est produite lors du traitement de votre demande. fail_html: 'Veuillez réessayer dans %{timeout}. Pour votre diff --git a/spec/controllers/idv/phone_errors_controller_spec.rb b/spec/controllers/idv/phone_errors_controller_spec.rb index 2191b8f32e2..e825c321435 100644 --- a/spec/controllers/idv/phone_errors_controller_spec.rb +++ b/spec/controllers/idv/phone_errors_controller_spec.rb @@ -62,6 +62,7 @@ end context 'the user is not authenticated and not recovering their account' do + let(:user) { nil } it 'redirects to sign in' do get action @@ -83,11 +84,17 @@ let(:idv_session) { double } let(:idv_session_user_phone_confirmation) { false } let(:user) { nil } + let(:phone) { '3602345678' } + let(:country_code) { 'US' } before do allow(idv_session).to receive(:user_phone_confirmation). and_return(idv_session_user_phone_confirmation) allow(idv_session).to receive(:current_user).and_return(user) + allow(idv_session).to receive(:previous_phone_step_params).and_return( + phone: phone, + international_code: country_code, + ) allow(subject).to receive(:remaining_attempts).and_return(5) allow(controller).to receive(:idv_session).and_return(idv_session) stub_sign_in(user) if user @@ -99,12 +106,21 @@ describe '#warning' do let(:action) { :warning } let(:template) { 'idv/phone_errors/warning' } + let(:user) { create(:user) } it_behaves_like 'an idv phone errors controller action' - context 'with throttle attempts' do - let(:user) { create(:user) } + it 'assigns phone' do + get action + expect(assigns(:phone)).to eql(phone) + end + it 'assigns country_code' do + get action + expect(assigns(:country_code)).to eql(country_code) + end + + context 'with throttle attempts' do before do Throttle.new(throttle_type: :proof_address, user: user).increment! end diff --git a/spec/features/idv/steps/phone_step_spec.rb b/spec/features/idv/steps/phone_step_spec.rb index 9b5a40d73e3..8392b3187e9 100644 --- a/spec/features/idv/steps/phone_step_spec.rb +++ b/spec/features/idv/steps/phone_step_spec.rb @@ -4,9 +4,15 @@ include IdvStepHelper include IdvHelper + let(:user) { user_with_2fa } + let(:gpo_enabled) { true } + + before do + allow(IdentityConfig.store).to receive(:enable_usps_verification).and_return(gpo_enabled) + end + context 'defaults on page load' do it 'selects sms delivery option by default', js: true do - user = user_with_2fa start_idv_from_sp complete_idv_steps_before_phone_step(user) expect(page).to have_checked_field( @@ -18,7 +24,6 @@ context 'with valid information' do it 'redirects to the otp confirmation step when the phone matches the 2fa phone number', js: true do - user = user_with_2fa start_idv_from_sp complete_idv_steps_before_phone_step(user) fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) @@ -77,8 +82,6 @@ end it 'is not re-entrant after confirming OTP' do - user = user_with_2fa - start_idv_from_sp complete_idv_steps_before_phone_step(user) fill_out_phone_form_ok @@ -121,39 +124,44 @@ expect(page).to have_current_path(idv_doc_auth_step_path(step: :welcome)) end - shared_examples 'async timed out' do - it 'allows resubmitting form' do - user = user_with_2fa - start_idv_from_sp - complete_idv_steps_before_phone_step(user) + it 'allows resubmitting form' do + start_idv_from_sp + complete_idv_steps_before_phone_step(user) - allow(DocumentCaptureSession).to receive(:find_by).and_return(nil) - fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) - click_idv_send_security_code - expect(page).to have_content(t('idv.failure.timeout')) - expect(page).to have_current_path(idv_phone_path) - allow(DocumentCaptureSession).to receive(:find_by).and_call_original - click_idv_send_security_code - expect(page).to have_current_path(idv_otp_verification_path) - end + allow(DocumentCaptureSession).to receive(:find_by).and_return(nil) + fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) + click_idv_send_security_code + expect(page).to have_content(t('idv.failure.timeout')) + expect(page).to have_current_path(idv_phone_path) + allow(DocumentCaptureSession).to receive(:find_by).and_call_original + click_idv_send_security_code + expect(page).to have_current_path(idv_otp_verification_path) end - it_behaves_like 'async timed out' - context "when the user's information cannot be verified" do + it 'reports the number the user entered' do + start_idv_from_sp + complete_idv_steps_before_phone_step + fill_out_phone_form_fail + click_idv_send_security_code + + expect(page).to have_content(t('idv.failure.phone.warning.heading')) + expect(page).to have_content('+1 703-555-5555') + end + it 'links to verify by mail, from which user can return back to the warning screen' do start_idv_from_sp complete_idv_steps_before_phone_step fill_out_phone_form_fail click_idv_send_security_code - expect(page).to have_content(t('idv.failure.phone.warning')) + expect(page).to have_content(t('idv.failure.phone.warning.heading')) - click_on t('idv.troubleshooting.options.verify_by_mail') + click_on t('idv.failure.phone.warning.gpo.button') expect(page).to have_content(t('idv.titles.mail.verify')) click_doc_auth_back_link - expect(page).to have_content(t('idv.failure.phone.warning')) + expect(page).to have_content(t('idv.failure.phone.warning.heading')) end it 'does not render the link to proof by mail if proofing by mail is disabled' do @@ -166,10 +174,10 @@ fill_out_phone_form_fail click_idv_send_security_code - expect(page).to have_content(t('idv.failure.phone.warning')) + expect(page).to have_content(t('idv.failure.phone.warning.heading')) expect(page).to_not have_content(t('idv.troubleshooting.options.verify_by_mail')) - click_on t('idv.failure.button.warning') + click_on t('idv.failure.phone.warning.try_again_button') end fill_out_phone_form_fail @@ -189,4 +197,40 @@ it_behaves_like 'verification step max attempts', :phone, :oidc it_behaves_like 'verification step max attempts', :phone, :saml end + + context 'when the user is rate-limited' do + before do + start_idv_from_sp + complete_idv_steps_before_step(:phone, user) + end + + around do |ex| + freeze_time { ex.run } + end + + before do + (Throttle.max_attempts(:proof_address) - 1).times do + fill_out_phone_form_fail + click_idv_continue_for_step(:phone) + click_on t('idv.failure.phone.warning.try_again_button') + end + click_idv_continue_for_step(:phone) + end + + it 'still lets them access the GPO flow and return to the error' do + click_on t('idv.failure.phone.rate_limited.gpo.button') + expect(page).to have_content(t('idv.titles.mail.verify')) + click_doc_auth_back_link + expect(page).to have_content(t('idv.failure.phone.rate_limited.heading')) + end + + context 'GPO is disabled' do + let(:gpo_enabled) { false } + + it 'does not link out to GPO flow' do + expect(page).not_to have_content(t('idv.failure.phone.rate_limited.gpo.prompt')) + expect(page).not_to have_content(t('idv.failure.phone.rate_limited.gpo.button')) + end + end + end end diff --git a/spec/support/idv_examples/max_attempts.rb b/spec/support/idv_examples/max_attempts.rb index 7f5e232ea09..cdf3c580f7c 100644 --- a/spec/support/idv_examples/max_attempts.rb +++ b/spec/support/idv_examples/max_attempts.rb @@ -1,12 +1,7 @@ shared_examples 'verification step max attempts' do |step, sp| include ActionView::Helpers::DateHelper - let(:locale) { LinkLocaleResolver.locale } let(:user) { user_with_2fa } - let(:step_locale_key) do - return :sessions if step == :profile - step - end before do start_idv_from_sp(sp) @@ -33,12 +28,12 @@ end scenario 'user sees the failure screen' do - expect(page).to have_content(t("idv.failure.#{step_locale_key}.heading")) + expect(page).to have_content(t('idv.failure.phone.rate_limited.heading')) expect(page).to have_content( strip_tags( t( - 'idv.failure.phone.fail_html', - timeout: distance_of_time_in_words( + 'idv.failure.phone.rate_limited.body', + time_left: distance_of_time_in_words( IdentityConfig.store.idv_attempt_window_in_hours.hours, ), ), @@ -52,7 +47,7 @@ (Throttle.max_attempts(:proof_address) - 1).times do fill_out_phone_form_fail click_idv_continue_for_step(step) - click_on t('idv.failure.button.warning') + click_on t('idv.failure.phone.warning.try_again_button') end fill_out_phone_form_ok @@ -67,15 +62,15 @@ def perfom_maximum_allowed_idv_step_attempts(step) (Throttle.max_attempts(:proof_address) - 1).times do yield click_idv_continue_for_step(step) - click_on t('idv.failure.button.warning') + click_on t('idv.failure.phone.warning.try_again_button') end yield click_idv_continue_for_step(step) end def expect_user_to_fail_at_phone_step - expect(page).to have_content(t("idv.failure.#{step_locale_key}.heading")) - expect(current_url).to eq(idv_phone_errors_failure_url(locale: locale)) - expect(page).to have_link(t('idv.troubleshooting.options.verify_by_mail')) + expect(page).to have_content(t('idv.failure.phone.rate_limited.heading')) + expect(current_url).to eq(idv_phone_errors_failure_url) + expect(page).to have_link(t('idv.failure.phone.rate_limited.gpo.button')) end end diff --git a/spec/views/idv/phone_errors/failure.html.erb_spec.rb b/spec/views/idv/phone_errors/failure.html.erb_spec.rb index 02ac43246e8..9367bae1c66 100644 --- a/spec/views/idv/phone_errors/failure.html.erb_spec.rb +++ b/spec/views/idv/phone_errors/failure.html.erb_spec.rb @@ -3,6 +3,7 @@ describe 'idv/phone_errors/failure.html.erb' do let(:sp_name) { 'Example SP' } let(:timeout_hours) { 6 } + let(:gpo_letter_available) { true } around do |ex| freeze_time { ex.run } @@ -11,7 +12,7 @@ before do decorated_session = instance_double(ServiceProviderSessionDecorator, sp_name: sp_name) allow(view).to receive(:decorated_session).and_return(decorated_session) - assign(:gpo_letter_available, true) + assign(:gpo_letter_available, gpo_letter_available) allow(IdentityConfig.store).to receive(:idv_attempt_window_in_hours).and_return(timeout_hours) @expires_at = Time.zone.now + timeout_hours.hours @@ -34,10 +35,36 @@ expect(rendered).to have_text( strip_tags( t( - 'idv.failure.phone.fail_html', - timeout: distance_of_time_in_words(timeout_hours.hours), + 'idv.failure.phone.rate_limited.body', + time_left: distance_of_time_in_words(timeout_hours.hours), ), ), ) end + + it 'describes GPO as an alternative' do + expect(rendered).to have_text(t('idv.failure.phone.rate_limited.gpo.prompt')) + end + + it 'includes a link to GPO flow' do + expect(rendered).to have_css( + '.usa-button', + text: t('idv.failure.phone.rate_limited.gpo.button'), + ) + end + + context 'GPO is not available' do + let(:gpo_letter_available) { false } + + it 'does not describe GPO as an alternative' do + expect(rendered).not_to have_text(t('idv.failure.phone.rate_limited.gpo.prompt')) + end + + it 'does not include a link to GPO flow' do + expect(rendered).not_to have_css( + '.usa-button', + text: t('idv.failure.phone.rate_limited.gpo.button'), + ) + end + end end diff --git a/spec/views/idv/phone_errors/warning.html.erb_spec.rb b/spec/views/idv/phone_errors/warning.html.erb_spec.rb index 71e20bc0a2e..cc555ff840b 100644 --- a/spec/views/idv/phone_errors/warning.html.erb_spec.rb +++ b/spec/views/idv/phone_errors/warning.html.erb_spec.rb @@ -4,27 +4,78 @@ let(:sp_name) { 'Example SP' } let(:remaining_attempts) { 5 } let(:gpo_letter_available) { false } + let(:phone) { '+13602345678' } + let(:country_code) { 'US' } + let(:formatted_phone) { '+1 360-234-5678' } before do decorated_session = instance_double(ServiceProviderSessionDecorator, sp_name: sp_name) allow(view).to receive(:decorated_session).and_return(decorated_session) assign(:gpo_letter_available, gpo_letter_available) - assign(:remaining_attempts, remaining_attempts) + assign(:country_code, country_code) + assign(:phone, phone) render end - it 'shows warning text' do - expect(rendered).to have_text(t('idv.failure.phone.warning')) + it 'shows correct h1' do + expect(rendered).to have_css('h1', text: t('idv.failure.phone.warning.heading')) end - it 'shows a primary action' do - expect(rendered).to have_link(t('idv.failure.button.warning'), href: idv_phone_path) + it 'shows number entered' do + expect(rendered).to have_text(t('idv.failure.phone.warning.you_entered')) + expect(rendered).to have_text(formatted_phone) + end + + it 'shows next steps' do + expect(rendered).to include(t('idv.failure.phone.warning.next_steps_html')) + end + + it 'links to help screen' do + expect(rendered).to have_link( + t('idv.failure.phone.warning.learn_more_link'), + href: help_center_redirect_path( + category: 'verify-your-identity', + article: 'phone-number-and-phone-plan-in-your-name', + flow: :idv, + step: :phone, + location: 'learn_more', + ), + ) end it 'shows remaining attempts' do - expect(rendered).to have_text(t('idv.failure.attempts', count: remaining_attempts)) + expect(rendered).to have_text( + t( + 'idv.failure.phone.warning.attempts', + count: remaining_attempts, + ), + ) + end + + it 'shows a primary action' do + expect(rendered).to have_link( + t('idv.failure.phone.warning.try_again_button'), + href: idv_phone_path, + ) + end + + it 'renders a list of troubleshooting options' do + expect(rendered).to have_link( + t('idv.troubleshooting.options.get_help_at_sp', sp_name: sp_name), + href: return_to_sp_failure_to_proof_path(step: 'phone', location: 'warning'), + ) + end + + context 'no sp' do + let(:sp_name) { nil } + it 'does not prompt user to get help at sp' do + expect(rendered).not_to have_link( + t('idv.troubleshooting.options.get_help_at_sp', sp_name: sp_name), + href: return_to_sp_failure_to_proof_path(step: 'phone', location: 'warning'), + ) + end end context 'gpo verification disabled' do @@ -33,6 +84,8 @@ t('idv.troubleshooting.options.get_help_at_sp', sp_name: sp_name), href: return_to_sp_failure_to_proof_path(step: 'phone', location: 'warning'), ) + end + it 'does not render link to gpo flow' do expect(rendered).not_to have_link( t('idv.troubleshooting.options.verify_by_mail'), href: idv_gpo_path, @@ -43,15 +96,25 @@ context 'gpo verification enabled' do let(:gpo_letter_available) { true } - it 'renders a list of troubleshooting options' do - expect(rendered).to have_link( - t('idv.troubleshooting.options.get_help_at_sp', sp_name: sp_name), - href: return_to_sp_failure_to_proof_path(step: 'phone', location: 'warning'), + it 'has an h2' do + expect(rendered).to have_css('h2', text: t('idv.failure.phone.warning.gpo.heading')) + end + + it 'explains gpo' do + expect(rendered).to have_text( + t('idv.failure.phone.warning.gpo.explanation'), ) - expect(rendered).to have_link( - t('idv.troubleshooting.options.verify_by_mail'), - href: idv_gpo_path, + end + + it 'says how long gpo takes' do + expect(rendered).to have_css( + 'strong', + text: t('idv.failure.phone.warning.gpo.how_long_it_takes'), ) end + + it 'has a secondary cta' do + expect(rendered).to have_link(t('idv.failure.phone.warning.gpo.button'), href: idv_gpo_path) + end end end diff --git a/spec/views/idv/shared/_error.html.erb_spec.rb b/spec/views/idv/shared/_error.html.erb_spec.rb index 3f5439ee979..8ab6c5f2225 100644 --- a/spec/views/idv/shared/_error.html.erb_spec.rb +++ b/spec/views/idv/shared/_error.html.erb_spec.rb @@ -7,12 +7,15 @@ let(:action) { nil } let(:action_secondary) { nil } let(:type) { nil } + let(:current_step) { nil } + let(:step_indicator_steps) { nil } let(:params) do { type: type, heading: heading, action: action, action_secondary: action_secondary, + current_step: current_step, options: options, } end @@ -21,6 +24,10 @@ decorated_session = instance_double(ServiceProviderSessionDecorator, sp_name: sp_name) allow(view).to receive(:decorated_session).and_return(decorated_session) + if step_indicator_steps + allow(view).to receive(:step_indicator_steps).and_return(step_indicator_steps) + end + render 'idv/shared/error', **params end @@ -163,4 +170,32 @@ end end end + + describe 'current_step' do + it 'does not render a step indicator by default' do + expect(view.content_for(:pre_flash_content)).not_to have_css('lg-step-indicator') + end + + context 'current_step provided' do + let(:current_step) { :verify_phone_or_address } + + it 'does not render a step indicator' do + expect(view.content_for(:pre_flash_content)).not_to have_css('lg-step-indicator') + end + + context 'step_indicator_steps helper available' do + let(:step_indicator_steps) { Idv::Flows::DocAuthFlow::STEP_INDICATOR_STEPS } + it 'renders a step indicator' do + expect(view.content_for(:pre_flash_content)).to have_css('lg-step-indicator') + end + + it 'selects the correct step' do + expect(view.content_for(:pre_flash_content)).to have_css( + '.step-indicator__step--current .step-indicator__step-title', + text: t('step_indicator.flows.idv.verify_phone_or_address'), + ) + end + end + end + end end From 33f285efeab3c499677a71efe95c7da449b2dce9 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Tue, 4 Apr 2023 11:01:17 -0500 Subject: [PATCH 02/25] Use ActiveSupport cache version 7.0 (#8126) changelog: Internal, Caching, Use ActiveSupport cache version 7.0 --- config/application.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/application.rb b/config/application.rb index e21996ebbcf..cb186ffee26 100644 --- a/config/application.rb +++ b/config/application.rb @@ -54,8 +54,6 @@ class Application < Rails::Application end config.load_defaults '7.0' - # Delete after deploying once - config.active_support.cache_format_version = 6.1 config.active_record.belongs_to_required_by_default = false config.active_record.legacy_connection_handling = false config.assets.unknown_asset_fallback = true From 783fdfaa07043f80f3359c855e57068cea213003 Mon Sep 17 00:00:00 2001 From: Tomas Apodaca Date: Tue, 4 Apr 2023 10:14:35 -0700 Subject: [PATCH 03/25] LG-8908: Add USPS document check to ProofingComponent when enrollment is created (#8113) * add usps doc check to proofing component earlier changelog: Internal, refactor, in-person VerifyInfoController outside Flow State Machine * include Steps::ThreadMetricStepHelper (see #7924) * replace IdvSession with IdvStepConcern (see #7840) and remove redundant confirm_two_factor_authenticate (see #8082) * give in-person verify info controller its own view and fix links to update pages * move process_async_state into the concern --- .../concerns/idv/verify_info_concern.rb | 23 +++ .../in_person/usps_locations_controller.rb | 17 +- .../idv/in_person/verify_info_controller.rb | 37 +---- app/controllers/idv/verify_info_controller.rb | 26 --- .../idv/steps/in_person/verify_step.rb | 7 - .../idv/in_person/verify_info/new.html.erb | 0 .../idv/in_person/verify_info/show.html.erb | 153 ++++++++++++++++++ app/views/idv/verify_info/show.html.erb | 22 +-- .../usps_locations_controller_spec.rb | 8 + .../in_person/verify_info_controller_spec.rb | 6 - .../idv/steps/in_person/verify_info_spec.rb | 1 + .../idv/steps/in_person/verify_step_spec.rb | 6 - 12 files changed, 201 insertions(+), 105 deletions(-) create mode 100644 app/views/idv/in_person/verify_info/new.html.erb create mode 100644 app/views/idv/in_person/verify_info/show.html.erb diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 2cbfd63dd82..4a8550ea3b0 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -86,6 +86,29 @@ def warning_url idv_session_errors_warning_url end + def process_async_state(current_async_state) + if current_async_state.none? + idv_session.invalidate_verify_info_step! + render :show + elsif current_async_state.in_progress? + render 'shared/wait' + elsif current_async_state.missing? + analytics.idv_proofing_resolution_result_missing + flash.now[:error] = I18n.t('idv.failure.timeout') + render :show + + delete_async + idv_session.invalidate_verify_info_step! + + log_idv_verification_submitted_event( + success: false, + failure_reason: { idv_verification: [:timeout] }, + ) + elsif current_async_state.done? + async_state_done(current_async_state) + end + end + def async_state_done(current_async_state) add_proofing_costs(current_async_state.result) form_response = idv_result_to_form_response( diff --git a/app/controllers/idv/in_person/usps_locations_controller.rb b/app/controllers/idv/in_person/usps_locations_controller.rb index 0f149e6736e..f85b6853b41 100644 --- a/app/controllers/idv/in_person/usps_locations_controller.rb +++ b/app/controllers/idv/in_person/usps_locations_controller.rb @@ -38,21 +38,28 @@ def index render json: response.to_json end - def proofer - @proofer ||= EnrollmentHelper.usps_proofer - end - # save the Post Office location the user selected to an enrollment def update enrollment.update!( selected_location_details: update_params.as_json, issuer: current_sp&.issuer, ) + add_proofing_component render json: { success: true }, status: :ok end - protected + private + + def proofer + @proofer ||= EnrollmentHelper.usps_proofer + end + + def add_proofing_component + ProofingComponent. + create_or_find_by(user: effective_user). + update(document_check: Idp::Constants::Vendors::USPS) + end def handle_error(err) remapped_error = case err diff --git a/app/controllers/idv/in_person/verify_info_controller.rb b/app/controllers/idv/in_person/verify_info_controller.rb index 3fef4d4b638..0443352c507 100644 --- a/app/controllers/idv/in_person/verify_info_controller.rb +++ b/app/controllers/idv/in_person/verify_info_controller.rb @@ -1,19 +1,17 @@ module Idv module InPerson class VerifyInfoController < ApplicationController - include IdvSession + include IdvStepConcern include StepIndicatorConcern include StepUtilitiesConcern + include Steps::ThreatMetrixStepHelper include VerifyInfoConcern before_action :renders_404_if_flag_not_set - before_action :confirm_two_factor_authenticated before_action :confirm_ssn_step_complete before_action :confirm_verify_info_step_needed def show - @in_person_proofing = true - @verify_info_submit_path = idv_in_person_verify_info_path @step_indicator_steps = step_indicator_steps increment_step_counts @@ -44,7 +42,6 @@ def update pii[:uuid_prefix] = ServiceProvider.find_by(issuer: sp_session[:issuer])&.app_id pii[:state_id_type] = 'drivers_license' unless pii.blank? - add_proofing_component ssn_throttle.increment! if ssn_throttle.throttled? @@ -95,12 +92,6 @@ def renders_404_if_flag_not_set render_not_found unless IdentityConfig.store.in_person_verify_info_controller_enabled end - def add_proofing_component - ProofingComponent. - create_or_find_by(user: current_user). - update(document_check: Idp::Constants::Vendors::USPS) - end - # copied from address_controller def confirm_ssn_step_complete return if pii.present? && pii[:ssn].present? @@ -147,30 +138,6 @@ def analytics_arguments irs_reproofing: irs_reproofing?, }.merge(**acuant_sdk_ab_test_analytics_args) end - - # copied from verify_base_step. May want reconciliation with phone_step - def process_async_state(current_async_state) - if current_async_state.none? - idv_session.resolution_successful = false - render 'idv/verify_info/show' - elsif current_async_state.in_progress? - render 'shared/wait' - elsif current_async_state.missing? - analytics.idv_proofing_resolution_result_missing - flash.now[:error] = I18n.t('idv.failure.timeout') - render 'idv/verify_info/show' - - delete_async - idv_session.resolution_successful = false - - log_idv_verification_submitted_event( - success: false, - failure_reason: { idv_verification: [:timeout] }, - ) - elsif current_async_state.done? - async_state_done(current_async_state) - end - end end end end diff --git a/app/controllers/idv/verify_info_controller.rb b/app/controllers/idv/verify_info_controller.rb index 9ea99d5ae5b..494ca2257e8 100644 --- a/app/controllers/idv/verify_info_controller.rb +++ b/app/controllers/idv/verify_info_controller.rb @@ -10,8 +10,6 @@ class VerifyInfoController < ApplicationController before_action :confirm_verify_info_step_needed def show - @in_person_proofing = false - @verify_info_submit_path = idv_verify_info_path @step_indicator_steps = step_indicator_steps increment_step_counts @@ -123,29 +121,5 @@ def current_flow_step_counts def increment_step_counts current_flow_step_counts['verify'] += 1 end - - # copied from verify_base_step. May want reconciliation with phone_step - def process_async_state(current_async_state) - if current_async_state.none? - idv_session.invalidate_verify_info_step! - render :show - elsif current_async_state.in_progress? - render 'shared/wait' - elsif current_async_state.missing? - analytics.idv_proofing_resolution_result_missing - flash.now[:error] = I18n.t('idv.failure.timeout') - render :show - - delete_async - idv_session.invalidate_verify_info_step! - - log_idv_verification_submitted_event( - success: false, - failure_reason: { idv_verification: [:timeout] }, - ) - elsif current_async_state.done? - async_state_done(current_async_state) - end - end end end diff --git a/app/services/idv/steps/in_person/verify_step.rb b/app/services/idv/steps/in_person/verify_step.rb index 43a80d1d2eb..a74454b3505 100644 --- a/app/services/idv/steps/in_person/verify_step.rb +++ b/app/services/idv/steps/in_person/verify_step.rb @@ -14,7 +14,6 @@ def self.analytics_submitted_event def call pii[:state_id_type] = 'drivers_license' unless invalid_state? - add_proofing_component enqueue_job end @@ -28,12 +27,6 @@ def extra_view_variables private - def add_proofing_component - ProofingComponent. - create_or_find_by(user: current_user). - update(document_check: Idp::Constants::Vendors::USPS) - end - def capture_secondary_id_enabled? current_user.establishing_in_person_enrollment.capture_secondary_id_enabled end diff --git a/app/views/idv/in_person/verify_info/new.html.erb b/app/views/idv/in_person/verify_info/new.html.erb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/views/idv/in_person/verify_info/show.html.erb b/app/views/idv/in_person/verify_info/show.html.erb new file mode 100644 index 00000000000..fd5c71f4ebb --- /dev/null +++ b/app/views/idv/in_person/verify_info/show.html.erb @@ -0,0 +1,153 @@ +<%# +locals: + @step_indicator_steps - the correct Idv::Flows variable for this flow + @pii - user's information + @had_barcode_read_failure - show warning if there's a barcode read error +%> + +<% content_for(:pre_flash_content) do %> + <%= render StepIndicatorComponent.new( + steps: @step_indicator_steps, + current_step: :verify_info, + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + ) %> +<% end %> + +
+ +
+ +<% if @had_barcode_read_failure %> + <%= render AlertComponent.new( + type: :warning, + class: 'margin-bottom-4', + text_tag: 'div', + ) do %> + <%= t( + 'doc_auth.headings.capture_scan_warning_html', + link: render( + FormLinkComponent.new( + href: idv_doc_auth_step_path(step: :redo_document_capture), + method: :put, + ).with_content(t('doc_auth.headings.capture_scan_warning_link')), + ), + ) %> + <% end %> +<% end %> + +<% title t('titles.idv.verify_info') %> + +<%= render PageHeadingComponent.new.with_content(t('headings.verify')) %> +
+
+
+
+
<%= t('idv.form.first_name') %>:
+
<%= @pii[:first_name] %>
+
+
+
<%= t('idv.form.last_name') %>:
+
<%= @pii[:last_name] %>
+
+
+
<%= t('idv.form.dob') %>:
+
+ <%= I18n.l(Date.parse(@pii[:dob]), format: I18n.t('time.formats.event_date')) %> +
+
+
+
<%= t('idv.form.id_number') %>:
+
<%= @pii[:state_id_number] %>
+
+
+
+ <%= button_to( + idv_in_person_step_url(step: :redo_state_id), + method: :put, + class: 'usa-button usa-button--unstyled', + 'aria-label': t('idv.buttons.change_state_id_label'), + ) { t('idv.buttons.change_label') } %> +
+
+
+
+
+
<%= t('idv.form.address1') %>:
+
<%= @pii[:address1] %>
+
+
+
<%= t('idv.form.address2') %>:
+
<%= @pii[:address2].presence %>
+
+
+
<%= t('idv.form.city') %>:
+
<%= @pii[:city] %>
+
+
+
<%= t('idv.form.state') %>:
+
<%= @pii[:state] %>
+
+
+
<%= t('idv.form.zipcode') %>:
+
<%= @pii[:zipcode] %>
+
+
+
+ <%= button_to( + idv_in_person_step_url(step: :redo_address), + method: :put, + class: 'usa-button usa-button--unstyled', + 'aria-label': t('idv.buttons.change_address_label'), + ) { t('idv.buttons.change_label') } %> +
+
+
+
+ <%= t('idv.form.ssn') %>: + <%= render( + 'shared/masked_text', + text: SsnFormatter.format(@pii[:ssn]), + masked_text: SsnFormatter.format_masked(@pii[:ssn]), + accessible_masked_text: t( + 'idv.accessible_labels.masked_ssn', + first_number: @pii[:ssn][0], + last_number: @pii[:ssn][-1], + ), + toggle_label: t('forms.ssn.show'), + ) %> +
+
+ <%= button_to( + idv_in_person_step_url(step: :redo_ssn), + method: :put, + class: 'usa-button usa-button--unstyled', + 'aria-label': t('idv.buttons.change_ssn_label'), + ) { t('idv.buttons.change_label') } %> +
+
+
+ <%= render SpinnerButtonComponent.new( + action: ->(**tag_options, &block) do + button_to(idv_in_person_verify_info_path, **tag_options, &block) + end, + big: true, + wide: true, + action_message: t('idv.messages.verifying'), + method: :put, + form: { + class: 'button_to', + data: { + form_steps_wait: '', + error_message: t('idv.failure.exceptions.internal_error'), + alert_target: '#form-steps-wait-alert', + wait_step_path: idv_in_person_verify_info_path, + poll_interval_ms: IdentityConfig.store.poll_rate_for_verify_in_seconds * 1000, + }, + }, + ).with_content(t('forms.buttons.continue')) %> +
+
+ +<% javascript_packs_tag_once 'form-steps-wait' %> +<%= render 'idv/doc_auth/cancel', step: 'verify' %> diff --git a/app/views/idv/verify_info/show.html.erb b/app/views/idv/verify_info/show.html.erb index 48843b1f3e7..40a4bfa6c29 100644 --- a/app/views/idv/verify_info/show.html.erb +++ b/app/views/idv/verify_info/show.html.erb @@ -1,10 +1,8 @@ <%# locals: - @verify_info_submit_path - either idv_verify_info_path or idv_in_person_verify_info_path @step_indicator_steps - the correct Idv::Flows variable for this flow @pii - user's information @had_barcode_read_failure - show warning if there's a barcode read error - @in_person_proofing - true for in person proofing %> <% content_for(:pre_flash_content) do %> @@ -58,23 +56,7 @@ locals: <%= I18n.l(Date.parse(@pii[:dob]), format: I18n.t('time.formats.event_date')) %> - <% if @in_person_proofing %> -
-
<%= t('idv.form.id_number') %>:
-
<%= @pii[:state_id_number] %>
-
- <% end %> - <% if @in_person_proofing %> -
- <%= button_to( - idv_in_person_step_url(step: :redo_state_id), - method: :put, - class: 'usa-button usa-button--unstyled', - 'aria-label': t('idv.buttons.change_state_id_label'), - ) { t('idv.buttons.change_label') } %> -
- <% end %>
@@ -134,7 +116,7 @@ locals:
<%= render SpinnerButtonComponent.new( action: ->(**tag_options, &block) do - button_to(@verify_info_submit_path, **tag_options, &block) + button_to(idv_verify_info_path, **tag_options, &block) end, big: true, wide: true, @@ -146,7 +128,7 @@ locals: form_steps_wait: '', error_message: t('idv.failure.exceptions.internal_error'), alert_target: '#form-steps-wait-alert', - wait_step_path: @verify_info_submit_path, + wait_step_path: idv_verify_info_path, poll_interval_ms: IdentityConfig.store.poll_rate_for_verify_in_seconds * 1000, }, }, diff --git a/spec/controllers/idv/in_person/usps_locations_controller_spec.rb b/spec/controllers/idv/in_person/usps_locations_controller_spec.rb index 1af6b68966a..c3b0c457bc1 100644 --- a/spec/controllers/idv/in_person/usps_locations_controller_spec.rb +++ b/spec/controllers/idv/in_person/usps_locations_controller_spec.rb @@ -257,6 +257,14 @@ expect(enrollment.service_provider).to be_nil end + it 'updates proofing component vendor' do + expect(user.proofing_component&.document_check).to be_nil + + response + + expect(user.proofing_component.document_check).to eq Idp::Constants::Vendors::USPS + end + context 'when unauthenticated' do let(:user) { nil } diff --git a/spec/controllers/idv/in_person/verify_info_controller_spec.rb b/spec/controllers/idv/in_person/verify_info_controller_spec.rb index 83ea59e23d9..a2dfa6ec3de 100644 --- a/spec/controllers/idv/in_person/verify_info_controller_spec.rb +++ b/spec/controllers/idv/in_person/verify_info_controller_spec.rb @@ -158,12 +158,6 @@ expect(response).to redirect_to(idv_session_errors_ssn_failure_url) end end - - it 'updates proofing component vendor' do - put :update - - expect(user.proofing_component.document_check).to eq Idp::Constants::Vendors::USPS - 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 7aacb6188f1..3fe16da670b 100644 --- a/spec/features/idv/steps/in_person/verify_info_spec.rb +++ b/spec/features/idv/steps/in_person/verify_info_spec.rb @@ -24,6 +24,7 @@ complete_ssn_step(user) # verify page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) expect(page).to have_current_path(idv_in_person_verify_info_path) expect(page).to have_content(t('headings.verify')) expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) diff --git a/spec/services/idv/steps/in_person/verify_step_spec.rb b/spec/services/idv/steps/in_person/verify_step_spec.rb index 61fac585527..e21f8070e92 100644 --- a/spec/services/idv/steps/in_person/verify_step_spec.rb +++ b/spec/services/idv/steps/in_person/verify_step_spec.rb @@ -141,11 +141,5 @@ def redirect(step_instance) expect(redirect(user2_step)).to eq(idv_session_errors_ssn_failure_url) end end - - it 'updates proofing component vendor' do - step.call - - expect(user.proofing_component.document_check).to eq Idp::Constants::Vendors::USPS - end end end From 71794b181cd9d099a41192b8516dde89c05f3a9e Mon Sep 17 00:00:00 2001 From: Jonathan Pirro Date: Tue, 4 Apr 2023 12:51:48 -0500 Subject: [PATCH 04/25] add/lock in saml_2023 references, remove saml_2021 ones (#8122) * saml_2022 -> saml_2023; remove saml_2021 stuff * changelog: Internal, SAML, remove saml2021 references (identity-devops#5739) * update example cert files in config/artifacts.example/local/ - remove the old saml2021 .crt and .key.enc files as they're no longer used/valid - change saml2023 .crt and .key.enc to actual files (vs. symlinks), newly created with openssl req -newkey (etc.) changelog: Internal, SAML, remove saml2021 references (identity-devops#5739) * Update specs to 2023 * Replace auth2022 Command: git grep -l auth2022 -- spec | xargs perl -p -i -e 's/auth2022/auth2023/g' -- * Replace logout2022 git grep -l logout2022 -- spec | xargs perl -p -i -e 's/logout2022/logout2023/g' -- * Update authpost2022 git grep -l authpost2022 -- spec | xargs perl -p -i -e 's/authpost2022/authpost2023/g' * Update one last spec --------- Co-authored-by: Zach Margolis --- config/application.yml.default | 4 +-- config/artifacts.example/local/saml2021.crt | 21 ------------ .../artifacts.example/local/saml2021.key.enc | 33 ------------------- config/artifacts.example/local/saml2023.crt | 22 ++++++++++++- .../artifacts.example/local/saml2023.key.enc | 31 ++++++++++++++++- config/initializers/app_artifacts.rb | 2 -- .../application_controller_spec.rb | 6 ++-- .../saml_completion_controller_spec.rb | 2 +- spec/controllers/saml_idp_controller_spec.rb | 28 ++++++++-------- .../reports/authorization_count_spec.rb | 2 +- spec/features/saml/multiple_endpoints_spec.rb | 2 +- .../saml/redirect_uri_validation_spec.rb | 2 +- spec/features/saml/saml_logout_spec.rb | 4 +-- spec/features/saml/saml_spec.rb | 2 +- spec/lib/app_artifacts_spec.rb | 10 +++--- spec/services/saml_endpoint_spec.rb | 16 ++++----- .../idv_examples/sp_requested_attributes.rb | 2 +- spec/support/saml_auth_helper.rb | 12 +++---- 18 files changed, 97 insertions(+), 104 deletions(-) delete mode 100644 config/artifacts.example/local/saml2021.crt delete mode 100644 config/artifacts.example/local/saml2021.key.enc mode change 120000 => 100644 config/artifacts.example/local/saml2023.crt mode change 120000 => 100644 config/artifacts.example/local/saml2023.key.enc diff --git a/config/application.yml.default b/config/application.yml.default index 1562ace5eba..3b67dda31c3 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -398,7 +398,7 @@ development: recurring_jobs_disabled_names: "[]" s3_report_bucket_prefix: '' s3_report_public_bucket_prefix: '' - saml_endpoint_configs: '[{"suffix":"2021","secret_key_passphrase":"trust-but-verify"},{"suffix":"2022","secret_key_passphrase":"trust-but-verify"}]' + saml_endpoint_configs: '[{"suffix":"2022","secret_key_passphrase":"trust-but-verify"},{"suffix":"2023","secret_key_passphrase":"trust-but-verify"}]' scrypt_cost: 10000$8$1$ secret_key_base: development_secret_key_base session_encryption_key: 27bad3c25711099429c1afdfd1890910f3b59f5a4faec1c85e945cb8b02b02f261ba501d99cfbb4fab394e0102de6fecf8ffe260f322f610db3e96b2a775c120 @@ -567,7 +567,7 @@ test: reset_password_email_window_in_minutes: 80 s3_report_bucket_prefix: '' s3_report_public_bucket_prefix: '' - saml_endpoint_configs: '[{"suffix":"2022","secret_key_passphrase":"trust-but-verify"}]' + saml_endpoint_configs: '[{"suffix":"2023","secret_key_passphrase":"trust-but-verify"}]' saml_internal_post: true scrypt_cost: 800$8$1$ secret_key_base: test_secret_key_base diff --git a/config/artifacts.example/local/saml2021.crt b/config/artifacts.example/local/saml2021.crt deleted file mode 100644 index a3a90976e56..00000000000 --- a/config/artifacts.example/local/saml2021.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDajCCAlICCQDiLwemRjMuPDANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJV -UzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hp -bmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292MRIwEAYDVQQD -DAlsb2NhbGhvc3QwHhcNMjEwMjI1MTYzMzU2WhcNMjIwNDAxMTYzMzU2WjB3MQsw -CQYDVQQGEwJVUzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV -BAcMCldhc2hpbmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292 -MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQDCBT+T3S3mCjqS/InvU5WwZAYnqUxkNrhzg1wNYUTgw+jpEOcOPCXXesAY -8IfVgQ/AQdMWsg6IQ3cFQG7ZkArVRsXVeJSTH0X91YOCZN3fZF34TQ+lizPppZY2 -sI/4rj255OblZqri09watuez/+L+OhkgZOCWZVIGAG5XZrAsXF3cMr3cU9eegux8 -oduJCL0UtLYqlRY3CCsJodBwDj2wOfhwfd+wMftFgQpbulqyRjMKOfJ89WHY9nZ3 -y40Gi4tEpuhaqqlsZwE128Zra+32ZPayv31KHHd5J7CpJx29fuRroV4M6CIf0Lou -Y5pBCvlBDjiZ3iS3GXf3A+7KL48jAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABMF -7eA2UogL9NBJgAg6+Tu09G/s8+hBhhFUl9/JgPzoxxMmumyqgc/SL1hroceDujFt -dndNCEhCOSxF88XO3JfjYcatxgVIIuIu4BrAnfrfICknNIav35J/46uY5g0qtDWU -ru3DVIQZzBwiKA+6pRt1VL4jHto1qZdEOJJeQTWcSPFPt/y6RTIKTDGs139yH96B -VyJNs+aqb4yMnGhYk9Y685uy+AO9iwJIMuc4U1q0eo/gzJuQZSK26T3eYlXdeTqL -6WnhnFtZtGMM6lPyI+nJxI8w/15Z0sDmFia6qIUCHgT1SBYFhdoUED5Uq/hpp2XF -/YfOQ2zUY9aYuZIWwF4= ------END CERTIFICATE----- diff --git a/config/artifacts.example/local/saml2021.key.enc b/config/artifacts.example/local/saml2021.key.enc deleted file mode 100644 index 6a424ee64de..00000000000 --- a/config/artifacts.example/local/saml2021.key.enc +++ /dev/null @@ -1,33 +0,0 @@ -This is a public example key used for testing and local development. -Even though it appears to be a private key, it is not secret and is intended to be public. - ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIT2qdiG8rutwCAggA -MB0GCWCGSAFlAwQBKgQQdshP+2DFwUsdi5iSAQIwHwSCBNAklzn3KzZPKYt+DQR7 -8619z59keEyGaK+Whptwy64vStLoBvUyMbLCyFx9fAEI+qibgBzep5mn8sRTLlui -MsmrE0xtJhsqeE3+U3jk7lyWfF/zk1ofmiBzVaUMLlBSbrV0fNj5PXjTeoAfTTDC -aXBiZ4N24YLs+lDeEv1g1ZX/JogVd689ClY9jTX14nL5eLkwqOu2febb7yA9gbmn -KwaeEdTTQqolcBH3GkdubBSGmovZkLFodnHN6o5ioFy3FxHveNxRne9i+AZ6xOx+ -rgU71wbp5EWlXB3iRqBSBVy/bqsdBlHlqou0cGxVyxVJOysPRfThC8F2ACzZuBR0 -LPJnexmhxHIl9mvNRgKujV7375HFLmK/2vAd4Ujs07ixpWQrwV1jExJkhbA8mVyj -9WPs5S40RojQDFMgbZZ+UDcT5DKIO9K9pHGoiAyhnRE+JZebGoozxRwcmMubQRmp -T0skC6LtBelf87HGMUzT5hWnIX8tch7HXrcG3/sawvlEs3+bm18dLu0ih/ejsRfE -2iukGzrriL/ivnXY09Red69QjuDs7oPYiYs0oFG1n3u2x6WuHp/wKHdiRi0Z3mdE -zfdNX5c4AW9QlJCj09urR0QX2GUSb/PuimzyN7IFHyCU79XW1jixH2q30BDBQ9Z2 -XFmziARLTGSJ4OTLn0If+PmTAekcGteS9se9nOZ2bFnMEq5YOIIEM0yikciDbBOM -nehCxQonWZF/1DYWNVklsjVd6mOe0wHEUwUUcAbjuqaDIi1UrhKsGBVL6PAdxFVC -Zu1zjABjUwYQPOhIzNSds1CXvCETNikrWDQNMQLMkWhBPiGf2ocHu+1qeLZb4tN1 -JG6I5l5ILQFVGlIme1kwCLnDxPZYul8b1IdlUG2HEdLN8rjhUYtKvqU7MKNqz+gm -AhG1juflPME9XnombtXW/qhZbvm4/Guf51wpZhd9s6hRm5xydCo01X9R4Qz9f0dB -hfTzNM6/GkKsMMTEyjTMryByb+E4AR6DYaFxfmmFIxcEOPQ3cOpwu9EGxL8tIx6t -Bqm5H/6TKGguVxRF4PNrTT/1r1c3TQOPstBQe/bMTb5+LHkxq423xPNaS7B4jTow -pUVrpCnHXBciQiDAdZy1VW2It/ilYaUkFDGAF7aATI8BzLlZ9mBC55IH5oW9hmdm -jd2wKS4OR3qfNIRBebHEinWNq0LvwRoGisPTdE+5QqO4l5nswbF4XTUQ7wue2jg3 -8m2K8WrTxrDZD40YtSjI4akWs+433C/SqmN4uKDCr5oTGjksDbM+gFcLGazROatU -dnoLHLRFF/PP4OYpjS+hALYXAagWQ2I4A/9ZAA4gODClJKFA9x6P88G76OaxDyOk -DR4kaRUW6GfnyJxeiY7YEiCNGl91LdjGNYsb06UOGF0+D+caeOgacgux88Lma+r7 -zSPoKR2ijDCVoU5CysR6pXNl3iPXZPj8aUrBAWozObUBB66XiYpWIZ+goqjGjJGm -/w23daBF57shAFge9tmbHJDeaRiju9M4C3UgLDFxmMf+GtywEBUBJ+K5xSVF08JO -jClUlvFTOLUY8QkOWyWgRs9NhaDUtctZElHR9KqnSd5aHr0+atwPzO4LlMYqyOIY -AHO2OUTG3hCglQDmDZ9W9M8Kog== ------END ENCRYPTED PRIVATE KEY----- diff --git a/config/artifacts.example/local/saml2023.crt b/config/artifacts.example/local/saml2023.crt deleted file mode 120000 index 3adf05b96cb..00000000000 --- a/config/artifacts.example/local/saml2023.crt +++ /dev/null @@ -1 +0,0 @@ -saml2022.crt \ No newline at end of file diff --git a/config/artifacts.example/local/saml2023.crt b/config/artifacts.example/local/saml2023.crt new file mode 100644 index 00000000000..914a417e994 --- /dev/null +++ b/config/artifacts.example/local/saml2023.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDajCCAlICCQCbZpJCM572hTANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJV +UzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hp +bmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292MRIwEAYDVQQD +DAlsb2dpbi5nb3YwHhcNMjMwNDA0MTYwNzIxWhcNMjQwNDAyMTYwNzIxWjB3MQsw +CQYDVQQGEwJVUzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV +BAcMCldhc2hpbmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292 +MRIwEAYDVQQDDAlsb2dpbi5nb3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCdToDm3/j0j0PMjca8bc7H0H3FNSTW4l6hpUSywkC/kg2fZ5W6f0hIYYID +TbDYAkpeIiKKE/FDI3TlaQT9+LLe6AbkelLmteS+wMehCPtaBPeRfHKaRNQKsSTk +c5JAf4OWaZKj+F3Fu0e5+dJ2nuYcT2VV7DLoG3KKTw+pcHuXCQZfrPbquyyNbKvo +K4ELVIueQQ5F3EiahP3XchGw+H5FCH5QJPVl57WaCB2gLM8kueELKIzta7roIYHf +GEhdaC71ZYCjGRvsKtAqomNdL2Je67E56dEwJ1fWS242PkSvQTH5vtkYzelE2H9m +V+sPf5lLfc599iLZoTemEe5p6NydAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAH88 +Yl+KbT3NPjrXH7SITWr1wOIemJ8b2/vukz7aS9TiTJnlw6IzsLnD+tiJa1q5CO23 +3/NCaa6piocbD1fC/H7PB6lXu+0ypMwpaStTThpxbpQ6bMUklxKKFyuaX5RpNZn4 +YicYGEnCr7h70+R/01ztgYNOzNdgM6MjHMvEnb8KVkuckuCE1JUoX+LxaE9cxkxX +Auwdct14efBFuyB2HgvWUvqvjN9NDhfS6BG5FgTZWpWJnn7xmjUNUfq1VC4XHQsv +mqoPiDhR1GwB191ZVz7Rq00yysfr7tSUJeWp//5GPZRjSZsrs1wtO6x/tFjngELl +d0/LPNS3OWvaMvvGzgc= +-----END CERTIFICATE----- diff --git a/config/artifacts.example/local/saml2023.key.enc b/config/artifacts.example/local/saml2023.key.enc deleted file mode 120000 index 35bacae1de4..00000000000 --- a/config/artifacts.example/local/saml2023.key.enc +++ /dev/null @@ -1 +0,0 @@ -saml2022.key.enc \ No newline at end of file diff --git a/config/artifacts.example/local/saml2023.key.enc b/config/artifacts.example/local/saml2023.key.enc new file mode 100644 index 00000000000..c1c1a03fa5d --- /dev/null +++ b/config/artifacts.example/local/saml2023.key.enc @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIfgN2KJrydvECAggA +MB0GCWCGSAFlAwQBKgQQEF8wtPY1ps4bJfQ4sV0xeQSCBND598BOsn9cAgqBGUEE +yQgCp+DIpDKFmgwXOiFjjuY1H3vsAFh0lhOoWN2z2UsAWUq8RwWs41UNSajgUllq +wPP3dm+U2BG661AmZEj5+fJ6ckCLX+BZHcv2bjx7cRF8oMv3Z1wlmTvxyARcFlct +b5Rxjl+moDwuBGFUaCg80hL2NZmUSuf/GZ5bj5yHZqGdc/KAXRts6knqLfsBxhrh +uECYUaz11NRN+JCNg8TcQSUJs01QVrhS7MrNhr1WGIwdIYg0AsqHqluzKr4nzjqG +u8vfRV7oraUhNDKrfXfxjGcyy/o4zYnuJO3zBRWPV4Ueesf3efGY3bNyZzoY2BrD +ZlpQwHf7NmANZqHnCAH9dDrk3mcNkfPEnSs/oguhIk05ZHMaZeGR+bIJd1LjxjLY +KOhgw9u+I4WR35GsZRFArwUnHfvAcLTwBbsChMk7ykj16F/rdIknMWiyZX3p31Cm +ZW/tB5j4lm9caA9iPn/d3S/H7O3zG3SSuwQvrwQguImqEegMq2FA6MLT+66R+DU/ +AB/M8WgwIhaLWr136GhGBjJO4Tm21DrNjHzS8NYWcXL8PWdDgagwbTGAL6jLLulr +iCrkEsQoNzX11WiwYgmhrPLKVqophHK4v3dzuY+naMV5GekqhPA+8IfWZfvmizb7 +LVoxb6TKBI0U3p31lhRI9car+Yxa7z5ZSAl0n03TCeZOYvUmKAwj81E23g1XC3Fx ++xB+nrDWQQjvD+ZRXQxqUg58o0qitM9tKsIkh2Qj4IRpATTyTyNO40seGni8AWsU +ftsll8OVWEdHlnOGLNjSPC/uVMYSa8YOcv+xIqErty2Q40yrOsXufX0ompzONNZH +rUbifoY3T8En88m7wCDBQAw38pUdgPniL0VyADdvkc4To5pjkK7B4g7fD+WbjdrD +V7UhU+4wxZkSK9ijaq/4Cj/ZIg5m29qOoCtI/vn2A/DnhLJmomQ8B21xgtUsa/pM +Pi1Z4rz2OLEND1EPuJSm/xijxVGUgYpkOYHoRQmvbpPst9FvXPIXcBdpahbVvqOq +Iv8LcKatTKqTjxyXoOxxUWQ16MYXaOSk8sEbDYqp/YK2N9ItIPiHiYio/Wt+uQM8 +/VbyZGY+e0O9mAGtoWLDuqCziH5HHXjH7sX0mJmrVCc8V7eKewEEYT6jCjvOMG+V +tABCJQXoaOV8Tk5f3rMDVYg+DQaqFfuvOEahj0CLm4xLuEK9mGvNz7HjxgPjRfS1 +CFn93oBKnhJbzeEkNEHWw2sG1/twiOjhSkMs5CbdcpAjm6wNoKE3/e23HbUuFGos +yOb/2mm5oAr/EjBCUSilyNSkIuUTzIKnootLJ4bxHmTHFlpJlYvFBKWT7RIRT21g +LErRAgUHF/f93yNjgEbdzf8lzKfYWBnZK1E2RGbVay3W1OtQQ7Om01CbKd0THt9y +4bwD/rS29+xa4NVImMXmq/aftLLoedaY9TWHz2FOInXhms9vw0wsehmc1aoQ1yrl +DVx+T7raO6YJ8MK385ryjupsZd4J5dYcvHjlkpnZOyvVyzfdNqHvVhtfd765d5rw +GyX2UKPByjECQskxKjeqadjZ8zaAecW/4ujg0wcmdX7l4lfXcdy6+0WiPr3qBwQE +hrcBC92oNEzyorgjdcMEz2RATw== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/config/initializers/app_artifacts.rb b/config/initializers/app_artifacts.rb index 3fb55aacb11..ae5ecd48941 100644 --- a/config/initializers/app_artifacts.rb +++ b/config/initializers/app_artifacts.rb @@ -2,8 +2,6 @@ AppArtifacts.setup do |store| # When adding or removing certs, make sure to update the 'saml_endpoint_configs' config - store.add_artifact(:saml_2021_cert, '/%s/saml2021.crt') - store.add_artifact(:saml_2021_key, '/%s/saml2021.key.enc') store.add_artifact(:saml_2022_cert, '/%s/saml2022.crt') store.add_artifact(:saml_2022_key, '/%s/saml2022.key.enc') store.add_artifact(:saml_2023_cert, '/%s/saml2023.crt') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 6ac9d6990cf..215f3da082f 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -402,7 +402,7 @@ def index end context 'with a SAML request' do - let(:sp_session_request_url) { '/api/saml/auth2022' } + let(:sp_session_request_url) { '/api/saml/auth2023' } it 'returns the saml completion url' do expect(url_with_updated_params).to eq complete_saml_url end @@ -440,9 +440,9 @@ def index context 'with saml_internal_post feature flag set to false' do before { allow(IdentityConfig.store).to receive(:saml_internal_post).and_return false } context 'with a SAML request' do - let(:sp_session_request_url) { '/api/saml/auth2022?SAMLRequest=blah' } + let(:sp_session_request_url) { '/api/saml/auth2023?SAMLRequest=blah' } it 'returns the original request url' do - expect(url_with_updated_params).to eq '/api/saml/auth2022?SAMLRequest=blah' + expect(url_with_updated_params).to eq '/api/saml/auth2023?SAMLRequest=blah' end end diff --git a/spec/controllers/saml_completion_controller_spec.rb b/spec/controllers/saml_completion_controller_spec.rb index ec38e62af35..fd6cbb8105c 100644 --- a/spec/controllers/saml_completion_controller_spec.rb +++ b/spec/controllers/saml_completion_controller_spec.rb @@ -20,7 +20,7 @@ Signature: signature, } end - let(:sp_session_request_url) { 'http://example.gov/api/saml/auth2022' } + let(:sp_session_request_url) { 'http://example.gov/api/saml/auth2023' } before do expect(controller).to receive(:sp_session).at_least(:once).and_return( diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index 456d874848f..27f9df1d08a 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -562,7 +562,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: authn_context, service_provider: sp1_issuer, - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -703,7 +703,7 @@ def name_id_version(format_urn) authn_context_comparison: 'minimum', requested_ial: 'ialmax', service_provider: sp1_issuer, - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1241,7 +1241,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: false, @@ -1282,7 +1282,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: 'none', service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1319,7 +1319,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1354,7 +1354,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: auth_settings.issuer, - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1426,7 +1426,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1458,7 +1458,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: auth_settings.issuer, - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1490,7 +1490,7 @@ def name_id_version(format_urn) authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -1505,7 +1505,7 @@ def name_id_version(format_urn) describe 'HEAD /api/saml/auth', type: :request do it 'responds with "403 Forbidden"' do - head '/api/saml/auth2022?SAMLRequest=bang!' + head '/api/saml/auth2023?SAMLRequest=bang!' expect(response.status).to eq(403) end @@ -1677,7 +1677,7 @@ def name_id_version(format_urn) ds: Saml::XML::Namespaces::SIGNATURE, ) - crt = AppArtifacts.store.saml_2022_cert + crt = AppArtifacts.store.saml_2023_cert expect(element.text).to eq(crt.split("\n")[1...-1].join("\n").delete("\n")) end @@ -1980,7 +1980,7 @@ def stub_auth authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: true, finish_profile: false, request_signed: false, @@ -2025,7 +2025,7 @@ def stub_requested_attributes authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: false, request_signed: true, @@ -2061,7 +2061,7 @@ def stub_requested_attributes authn_context_comparison: 'exact', requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, service_provider: 'http://localhost:3000', - endpoint: '/api/saml/auth2022', + endpoint: '/api/saml/auth2023', idv: false, finish_profile: true, request_signed: true, diff --git a/spec/features/reports/authorization_count_spec.rb b/spec/features/reports/authorization_count_spec.rb index 525a4e0f340..034add6b303 100644 --- a/spec/features/reports/authorization_count_spec.rb +++ b/spec/features/reports/authorization_count_spec.rb @@ -306,7 +306,7 @@ def expect_ial1_and_ial2_count(issuer) def reset_monthly_auth_count_and_login(user) SpReturnLog.delete_all - visit api_saml_logout2022_path + visit api_saml_logout2023_path sign_in_live_with_2fa(user) end end diff --git a/spec/features/saml/multiple_endpoints_spec.rb b/spec/features/saml/multiple_endpoints_spec.rb index 53f9e82a694..63ca9f0e2e7 100644 --- a/spec/features/saml/multiple_endpoints_spec.rb +++ b/spec/features/saml/multiple_endpoints_spec.rb @@ -4,7 +4,7 @@ include SamlAuthHelper include IdvHelper - let(:endpoint_suffix) { '2022' } + let(:endpoint_suffix) { '2023' } let(:user) { create(:user, :signed_up) } let(:endpoint_saml_settings) do diff --git a/spec/features/saml/redirect_uri_validation_spec.rb b/spec/features/saml/redirect_uri_validation_spec.rb index bbe608042e1..75a15ae18c6 100644 --- a/spec/features/saml/redirect_uri_validation_spec.rb +++ b/spec/features/saml/redirect_uri_validation_spec.rb @@ -6,7 +6,7 @@ context 'when redirect_uri param is included in SAML request' do it 'uses the return_to_sp_url URL and not the redirect_uri' do user = create(:user, :signed_up) - visit api_saml_auth2022_path( + visit api_saml_auth2023_path( SAMLRequest: CGI.unescape(saml_request(saml_settings)), redirect_uri: 'http://evil.com', state: '123abc', diff --git a/spec/features/saml/saml_logout_spec.rb b/spec/features/saml/saml_logout_spec.rb index caed7d6f1ae..69cf3d71516 100644 --- a/spec/features/saml/saml_logout_spec.rb +++ b/spec/features/saml/saml_logout_spec.rb @@ -120,7 +120,7 @@ }, ) - expect(current_path).to eq(api_saml_logout2022_path) + expect(current_path).to eq(api_saml_logout2023_path) expect(page.driver.status_code).to eq(400) # The user should be signed in @@ -134,7 +134,7 @@ it 'logs the user out and redirects to the sign in page' do sign_in_and_2fa_user(user) - visit api_saml_logout2022_path + visit api_saml_logout2023_path expect(page).to have_content(t('devise.sessions.signed_out')) expect(page).to have_current_path(root_path) diff --git a/spec/features/saml/saml_spec.rb b/spec/features/saml/saml_spec.rb index 0d6c2cd56c3..36febbce457 100644 --- a/spec/features/saml/saml_spec.rb +++ b/spec/features/saml/saml_spec.rb @@ -226,7 +226,7 @@ it 'redirects to root' do travel(Devise.timeout_in + 1.second) do - visit api_saml_logout2022_url + visit api_saml_logout2023_url expect(page.current_path).to eq('/') end end diff --git a/spec/lib/app_artifacts_spec.rb b/spec/lib/app_artifacts_spec.rb index c6331545552..4df950e0f0c 100644 --- a/spec/lib/app_artifacts_spec.rb +++ b/spec/lib/app_artifacts_spec.rb @@ -43,10 +43,10 @@ context 'when running locally' do it 'reads the artifact from the example folder' do store = instance.build do |store| - store.add_artifact(:test_artifact, '/%s/saml2021.crt') + store.add_artifact(:test_artifact, '/%s/saml2022.crt') end - file_path = Rails.root.join('config', 'artifacts.example', 'local', 'saml2021.crt') + file_path = Rails.root.join('config', 'artifacts.example', 'local', 'saml2022.crt') contents = File.read(file_path) expect(store.test_artifact).to eq(contents) expect(store['test_artifact']).to eq(contents) @@ -65,12 +65,12 @@ it 'allows a block to be used to transform values' do store = instance.build do |store| - store.add_artifact(:test_artifact, '/%s/saml2021.crt') do |cert| + store.add_artifact(:test_artifact, '/%s/saml2022.crt') do |cert| OpenSSL::X509::Certificate.new(cert) end end - file_path = Rails.root.join('config', 'artifacts.example', 'local', 'saml2021.crt') + file_path = Rails.root.join('config', 'artifacts.example', 'local', 'saml2022.crt') contents = File.read(file_path) expect(store.test_artifact).to be_a(OpenSSL::X509::Certificate) expect(store.test_artifact.to_pem).to eq(contents) @@ -80,7 +80,7 @@ describe '#method_missing' do it 'runs methods based on the configd artifact keys' do store = instance.build do |store| - store.add_artifact(:test_artifact, '/%s/saml2021.crt') + store.add_artifact(:test_artifact, '/%s/saml2022.crt') end expect { store.test_artifact }.to_not raise_error diff --git a/spec/services/saml_endpoint_spec.rb b/spec/services/saml_endpoint_spec.rb index ca059b822ab..8f3b4f4834d 100644 --- a/spec/services/saml_endpoint_spec.rb +++ b/spec/services/saml_endpoint_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe SamlEndpoint do - let(:path) { '/api/saml/auth2022' } + let(:path) { '/api/saml/auth2023' } let(:request) do request_double = double allow(request_double).to receive(:path).and_return(path) @@ -14,7 +14,7 @@ it 'should list the suffixes that are configured' do result = described_class.suffixes - expect(result).to eq(%w[2022]) + expect(result).to eq(%w[2023]) end end @@ -24,7 +24,7 @@ expect(result).to eq( [ - { suffix: '2022', secret_key_passphrase: 'trust-but-verify' }, + { suffix: '2023', secret_key_passphrase: 'trust-but-verify' }, ], ) end @@ -36,7 +36,7 @@ subject.secret_key.to_pem, ).to eq( OpenSSL::PKey::RSA.new( - AppArtifacts.store.saml_2022_key, + AppArtifacts.store.saml_2023_key, 'trust-but-verify', ).to_pem, ) @@ -66,7 +66,7 @@ expect( subject.x509_certificate, ).to eq( - AppArtifacts.store.saml_2022_cert, + AppArtifacts.store.saml_2023_cert, ) end end @@ -75,7 +75,7 @@ it 'returns the saml metadata with the suffix added to the urls' do result = subject.saml_metadata - expect(result.configurator.single_service_post_location).to match(%r{api/saml/auth2022\Z}) + expect(result.configurator.single_service_post_location).to match(%r{api/saml/auth2023\Z}) end it 'does not include the SingLogoutService endpoints when configured' do @@ -93,10 +93,10 @@ result = subject.saml_metadata expect(result.configurator.single_logout_service_post_location).to match( - %r{api/saml/logout2022\Z}, + %r{api/saml/logout2023\Z}, ) expect(result.configurator.remote_logout_service_post_location).to match( - %r{api/saml/remotelogout2022\Z}, + %r{api/saml/remotelogout2023\Z}, ) end end diff --git a/spec/support/idv_examples/sp_requested_attributes.rb b/spec/support/idv_examples/sp_requested_attributes.rb index e06efed3f06..f61de453275 100644 --- a/spec/support/idv_examples/sp_requested_attributes.rb +++ b/spec/support/idv_examples/sp_requested_attributes.rb @@ -71,7 +71,7 @@ if javascript_enabled? expect(current_path).to eq(test_saml_decode_assertion_path) else - expect(current_url).to include(api_saml_auth2022_url) + expect(current_url).to include(api_saml_auth2023_url) end end end diff --git a/spec/support/saml_auth_helper.rb b/spec/support/saml_auth_helper.rb index 0973f2a1993..a0fe9c8c2ca 100644 --- a/spec/support/saml_auth_helper.rb +++ b/spec/support/saml_auth_helper.rb @@ -23,8 +23,8 @@ def saml_settings(overrides: {}) settings.double_quote_xml_attribute_values = true # IdP setting - settings.idp_sso_target_url = "http://#{IdentityConfig.store.domain_name}/api/saml/auth2022" - settings.idp_slo_target_url = "http://#{IdentityConfig.store.domain_name}/api/saml/logout2022" + settings.idp_sso_target_url = "http://#{IdentityConfig.store.domain_name}/api/saml/auth2023" + settings.idp_slo_target_url = "http://#{IdentityConfig.store.domain_name}/api/saml/logout2023" settings.idp_cert_fingerprint = idp_fingerprint settings.idp_cert_fingerprint_algorithm = 'http://www.w3.org/2001/04/xmlenc#sha256' @@ -79,7 +79,7 @@ def saml_logout_request_url(overrides: {}, params: {}) end def saml_remote_logout_request_url(overrides: {}, params: {}) - overrides[:idp_slo_target_url] = "http://#{IdentityConfig.store.domain_name}/api/saml/remotelogout2022" + overrides[:idp_slo_target_url] = "http://#{IdentityConfig.store.domain_name}/api/saml/remotelogout2023" logout_request.create( saml_settings(overrides: overrides), params, @@ -108,12 +108,12 @@ def saml_get_auth(settings) def saml_post_auth(saml_request) # POST redirect binding Authn Request - request.path = '/api/saml/authpost2022' + request.path = '/api/saml/authpost2023' post :auth, params: { SAMLRequest: CGI.unescape(saml_request) } end def saml_final_post_auth(saml_request) - request.path = '/api/saml/finalauthpost2022' + request.path = '/api/saml/finalauthpost2023' post :auth, params: { SAMLRequest: CGI.unescape(saml_request) } end @@ -130,7 +130,7 @@ def saml_test_sp_key end def saml_test_idp_cert - AppArtifacts.store.saml_2022_cert + AppArtifacts.store.saml_2023_cert end public From a56a46680a3bf2d73f20f04b30c524890a04b4ba Mon Sep 17 00:00:00 2001 From: Matt Hinz Date: Tue, 4 Apr 2023 11:04:56 -0700 Subject: [PATCH 05/25] LG-9026: Additional cleanup (#8127) * Fix link to help center Link "Learn more about what phone number to use" to https://login.gov/help/verify-your-identity/phone-number/ [skip changelog] * Fix size of "Verify by mail " button --- app/views/idv/phone_errors/warning.html.erb | 4 +++- spec/views/idv/phone_errors/warning.html.erb_spec.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/idv/phone_errors/warning.html.erb b/app/views/idv/phone_errors/warning.html.erb index 54b57c45b5f..9a2005bd2fa 100644 --- a/app/views/idv/phone_errors/warning.html.erb +++ b/app/views/idv/phone_errors/warning.html.erb @@ -27,7 +27,7 @@ t('idv.failure.phone.warning.learn_more_link'), help_center_redirect_path( category: 'verify-your-identity', - article: 'phone-number-and-phone-plan-in-your-name', + article: 'phone-number', flow: :idv, step: :phone, location: 'learn_more', @@ -60,6 +60,8 @@
<%= render ButtonComponent.new( action: ->(**tag_options, &block) { link_to idv_gpo_path, **tag_options, &block }, + big: true, + wide: true, outline: true, ).with_content(t('idv.failure.phone.warning.gpo.button')) %>
diff --git a/spec/views/idv/phone_errors/warning.html.erb_spec.rb b/spec/views/idv/phone_errors/warning.html.erb_spec.rb index cc555ff840b..62a1e929a99 100644 --- a/spec/views/idv/phone_errors/warning.html.erb_spec.rb +++ b/spec/views/idv/phone_errors/warning.html.erb_spec.rb @@ -37,7 +37,7 @@ t('idv.failure.phone.warning.learn_more_link'), href: help_center_redirect_path( category: 'verify-your-identity', - article: 'phone-number-and-phone-plan-in-your-name', + article: 'phone-number', flow: :idv, step: :phone, location: 'learn_more', From 1398aba982002def4c2ca91ece247aebbc406c92 Mon Sep 17 00:00:00 2001 From: Sonia Connolly Date: Tue, 4 Apr 2023 12:24:30 -0700 Subject: [PATCH 06/25] Combine two versions of `confirm_document_capture_complete` before action (#8123) Combine the versions of confirm_document_capture_complete in IdvStepConcern (used by SsnController) and in AddressController. This will make it easier to continue updating that method as we continue adding functionality to DocumentCaptureController. Added method pii_from_doc to IdvStepConcern pii is no longer an instance variable and no longer memoized in SsnController and AddressController The long term plan is to move all the step-related before actions and methods to IdvStepConcern Move flow_session and flow_path to IdvStepConcern changelog: Internal, Flow State Machine replacement, combine similar before actions --------- Co-authored-by: Douglas Price --- .../concerns/idv/step_utilities_concern.rb | 9 --------- app/controllers/concerns/idv_step_concern.rb | 18 +++++++++++++---- app/controllers/idv/address_controller.rb | 20 +++---------------- .../idv/document_capture_controller.rb | 1 + app/controllers/idv/ssn_controller.rb | 2 +- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/app/controllers/concerns/idv/step_utilities_concern.rb b/app/controllers/concerns/idv/step_utilities_concern.rb index 59e03150f23..cd065b15560 100644 --- a/app/controllers/concerns/idv/step_utilities_concern.rb +++ b/app/controllers/concerns/idv/step_utilities_concern.rb @@ -2,15 +2,6 @@ module Idv module StepUtilitiesConcern extend ActiveSupport::Concern - def flow_session - user_session['idv/doc_auth'] - end - - # copied from doc_auth_controller - def flow_path - flow_session[:flow_path] - end - # Copied from capture_doc_flow.rb # and from doc_auth_flow.rb def acuant_sdk_ab_test_analytics_args diff --git a/app/controllers/concerns/idv_step_concern.rb b/app/controllers/concerns/idv_step_concern.rb index 9f10dfdf291..fd12ca56b90 100644 --- a/app/controllers/concerns/idv_step_concern.rb +++ b/app/controllers/concerns/idv_step_concern.rb @@ -8,11 +8,21 @@ module IdvStepConcern before_action :confirm_idv_needed end - def confirm_document_capture_complete - @pii = flow_session&.[]('pii_from_doc') # hash with indifferent access - return if @pii.present? + def flow_session + user_session['idv/doc_auth'] + end + + def pii_from_doc + flow_session&.[]('pii_from_doc') + end - flow_path = flow_session&.[](:flow_path) + # copied from doc_auth_controller + def flow_path + flow_session&.[](:flow_path) + end + + def confirm_document_capture_complete + return if pii_from_doc.present? if IdentityConfig.store.doc_auth_document_capture_controller_enabled && flow_path == 'standard' diff --git a/app/controllers/idv/address_controller.rb b/app/controllers/idv/address_controller.rb index 9c07d69ed6c..29acabfd2ca 100644 --- a/app/controllers/idv/address_controller.rb +++ b/app/controllers/idv/address_controller.rb @@ -1,14 +1,14 @@ module Idv class AddressController < ApplicationController include IdvSession + include IdvStepConcern - before_action :confirm_two_factor_authenticated before_action :confirm_document_capture_complete def new analytics.idv_address_visit - @presenter = AddressPresenter.new(pii: @pii) + @presenter = AddressPresenter.new(pii: pii_from_doc) end def update @@ -24,22 +24,8 @@ def update private - def confirm_document_capture_complete - @pii = user_session.dig('idv/doc_auth', 'pii_from_doc') - return if @pii.present? - - flow_path = user_session.dig('idv/doc_auth', :flow_path) - - if IdentityConfig.store.doc_auth_document_capture_controller_enabled && - flow_path == 'standard' - redirect_to idv_document_capture_url - else - redirect_to idv_doc_auth_url - end - end - def idv_form - Idv::AddressForm.new(@pii) + Idv::AddressForm.new(pii_from_doc) end def success diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 36aa13bfd55..93c9f45a4c9 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -1,6 +1,7 @@ module Idv class DocumentCaptureController < ApplicationController include IdvSession + include IdvStepConcern include StepIndicatorConcern include StepUtilitiesConcern include DocumentCaptureConcern diff --git a/app/controllers/idv/ssn_controller.rb b/app/controllers/idv/ssn_controller.rb index 25945d0373f..936b331d6f3 100644 --- a/app/controllers/idv/ssn_controller.rb +++ b/app/controllers/idv/ssn_controller.rb @@ -56,7 +56,7 @@ def extra_view_variables private def next_url - if @pii[:state] == 'PR' + if pii_from_doc[:state] == 'PR' idv_address_url else idv_verify_info_url From 886f937e54f91d45677b5ea96efe966fe5f8ff2e Mon Sep 17 00:00:00 2001 From: Tim Bradley <90272033+NavaTim@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:09:48 -0700 Subject: [PATCH 07/25] LG-8932: Fix check for state vs residential address; update test (#8124) [skip changelog] --- app/services/usps_in_person_proofing/enrollment_helper.rb | 2 +- .../usps_in_person_proofing/enrollment_helper_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/usps_in_person_proofing/enrollment_helper.rb b/app/services/usps_in_person_proofing/enrollment_helper.rb index 1c7f09d77f7..7bdf92559ef 100644 --- a/app/services/usps_in_person_proofing/enrollment_helper.rb +++ b/app/services/usps_in_person_proofing/enrollment_helper.rb @@ -48,7 +48,7 @@ def create_usps_enrollment(enrollment, pii) # If we're using secondary ID capture (aka double address verification), # then send the state ID address to USPS. Otherwise send the residential address. - if enrollment.capture_secondary_id_enabled? && !pii[:same_address_as_id] + if enrollment.capture_secondary_id_enabled? && !enrollment.current_address_matches_id? pii = pii.except(*SECONDARY_ID_ADDRESS_MAP.values). transform_keys(SECONDARY_ID_ADDRESS_MAP) end diff --git a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb index b8309908231..3dcc9274825 100644 --- a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb +++ b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb @@ -9,7 +9,7 @@ let(:pii) do Pii::Attributes.new_from_hash( Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE. - merge(same_address_as_id: current_address_matches_id). + merge(same_address_as_id: current_address_matches_id ? 'true' : 'false'). transform_keys(&:to_s), ) end @@ -101,7 +101,7 @@ let(:pii) do Pii::Attributes.new_from_hash( Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE. - merge(same_address_as_id: current_address_matches_id). + merge(same_address_as_id: current_address_matches_id ? 'true' : 'false'). merge(Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS). transform_keys(&:to_s), ) From 57d6588e6c5f7ad442a30d3f46102f0e7a06c618 Mon Sep 17 00:00:00 2001 From: "Luis H. Matos" Date: Tue, 4 Apr 2023 17:23:11 -0500 Subject: [PATCH 08/25] LG-9146 Fix intermittent infinity loop (#8133) changelog: Internal, Attempts API, Batch Job improvements --- spec/jobs/irs_attempts_events_batch_job_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/jobs/irs_attempts_events_batch_job_spec.rb b/spec/jobs/irs_attempts_events_batch_job_spec.rb index 1934641fcf9..ad1e15be1b4 100644 --- a/spec/jobs/irs_attempts_events_batch_job_spec.rb +++ b/spec/jobs/irs_attempts_events_batch_job_spec.rb @@ -79,8 +79,6 @@ allow(IdentityConfig.store).to receive(:irs_attempt_api_enabled).and_return(true) allow(IdentityConfig.store).to receive(:irs_attempt_api_aws_s3_enabled).and_return(true) - allow_any_instance_of(described_class).to receive(:reasonable_timespan?).and_return(true) - allow(IdentityConfig.store).to receive(:irs_attempt_api_public_key). and_return(encoded_public_key) From 2136e1ff144cafce60cdde6ca468042dde54f99c Mon Sep 17 00:00:00 2001 From: Jonathan Pirro Date: Tue, 4 Apr 2023 21:22:10 -0500 Subject: [PATCH 09/25] re-add saml2021* files to AppArtifacts.setup, for now (#8129) * re-add saml2021* files to AppArtifacts.setup, for now * changelog: Internal, SAML, re-add saml2021 references (identity-devops#5739) * re-add saml2021 files to config/artifacts.example/local/ changelog: Internal, SAML, remove saml2021 references (identity-devops#5739) --- config/artifacts.example/local/saml2021.crt | 21 ++++++++++++ .../artifacts.example/local/saml2021.key.enc | 33 +++++++++++++++++++ config/initializers/app_artifacts.rb | 2 ++ 3 files changed, 56 insertions(+) create mode 100644 config/artifacts.example/local/saml2021.crt create mode 100644 config/artifacts.example/local/saml2021.key.enc diff --git a/config/artifacts.example/local/saml2021.crt b/config/artifacts.example/local/saml2021.crt new file mode 100644 index 00000000000..a3a90976e56 --- /dev/null +++ b/config/artifacts.example/local/saml2021.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDajCCAlICCQDiLwemRjMuPDANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJV +UzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hp +bmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292MRIwEAYDVQQD +DAlsb2NhbGhvc3QwHhcNMjEwMjI1MTYzMzU2WhcNMjIwNDAxMTYzMzU2WjB3MQsw +CQYDVQQGEwJVUzEdMBsGA1UECAwURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV +BAcMCldhc2hpbmd0b24xDDAKBgNVBAoMA0dTQTESMBAGA1UECwwJTG9naW4uZ292 +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDCBT+T3S3mCjqS/InvU5WwZAYnqUxkNrhzg1wNYUTgw+jpEOcOPCXXesAY +8IfVgQ/AQdMWsg6IQ3cFQG7ZkArVRsXVeJSTH0X91YOCZN3fZF34TQ+lizPppZY2 +sI/4rj255OblZqri09watuez/+L+OhkgZOCWZVIGAG5XZrAsXF3cMr3cU9eegux8 +oduJCL0UtLYqlRY3CCsJodBwDj2wOfhwfd+wMftFgQpbulqyRjMKOfJ89WHY9nZ3 +y40Gi4tEpuhaqqlsZwE128Zra+32ZPayv31KHHd5J7CpJx29fuRroV4M6CIf0Lou +Y5pBCvlBDjiZ3iS3GXf3A+7KL48jAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABMF +7eA2UogL9NBJgAg6+Tu09G/s8+hBhhFUl9/JgPzoxxMmumyqgc/SL1hroceDujFt +dndNCEhCOSxF88XO3JfjYcatxgVIIuIu4BrAnfrfICknNIav35J/46uY5g0qtDWU +ru3DVIQZzBwiKA+6pRt1VL4jHto1qZdEOJJeQTWcSPFPt/y6RTIKTDGs139yH96B +VyJNs+aqb4yMnGhYk9Y685uy+AO9iwJIMuc4U1q0eo/gzJuQZSK26T3eYlXdeTqL +6WnhnFtZtGMM6lPyI+nJxI8w/15Z0sDmFia6qIUCHgT1SBYFhdoUED5Uq/hpp2XF +/YfOQ2zUY9aYuZIWwF4= +-----END CERTIFICATE----- diff --git a/config/artifacts.example/local/saml2021.key.enc b/config/artifacts.example/local/saml2021.key.enc new file mode 100644 index 00000000000..6a424ee64de --- /dev/null +++ b/config/artifacts.example/local/saml2021.key.enc @@ -0,0 +1,33 @@ +This is a public example key used for testing and local development. +Even though it appears to be a private key, it is not secret and is intended to be public. + +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIT2qdiG8rutwCAggA +MB0GCWCGSAFlAwQBKgQQdshP+2DFwUsdi5iSAQIwHwSCBNAklzn3KzZPKYt+DQR7 +8619z59keEyGaK+Whptwy64vStLoBvUyMbLCyFx9fAEI+qibgBzep5mn8sRTLlui +MsmrE0xtJhsqeE3+U3jk7lyWfF/zk1ofmiBzVaUMLlBSbrV0fNj5PXjTeoAfTTDC +aXBiZ4N24YLs+lDeEv1g1ZX/JogVd689ClY9jTX14nL5eLkwqOu2febb7yA9gbmn +KwaeEdTTQqolcBH3GkdubBSGmovZkLFodnHN6o5ioFy3FxHveNxRne9i+AZ6xOx+ +rgU71wbp5EWlXB3iRqBSBVy/bqsdBlHlqou0cGxVyxVJOysPRfThC8F2ACzZuBR0 +LPJnexmhxHIl9mvNRgKujV7375HFLmK/2vAd4Ujs07ixpWQrwV1jExJkhbA8mVyj +9WPs5S40RojQDFMgbZZ+UDcT5DKIO9K9pHGoiAyhnRE+JZebGoozxRwcmMubQRmp +T0skC6LtBelf87HGMUzT5hWnIX8tch7HXrcG3/sawvlEs3+bm18dLu0ih/ejsRfE +2iukGzrriL/ivnXY09Red69QjuDs7oPYiYs0oFG1n3u2x6WuHp/wKHdiRi0Z3mdE +zfdNX5c4AW9QlJCj09urR0QX2GUSb/PuimzyN7IFHyCU79XW1jixH2q30BDBQ9Z2 +XFmziARLTGSJ4OTLn0If+PmTAekcGteS9se9nOZ2bFnMEq5YOIIEM0yikciDbBOM +nehCxQonWZF/1DYWNVklsjVd6mOe0wHEUwUUcAbjuqaDIi1UrhKsGBVL6PAdxFVC +Zu1zjABjUwYQPOhIzNSds1CXvCETNikrWDQNMQLMkWhBPiGf2ocHu+1qeLZb4tN1 +JG6I5l5ILQFVGlIme1kwCLnDxPZYul8b1IdlUG2HEdLN8rjhUYtKvqU7MKNqz+gm +AhG1juflPME9XnombtXW/qhZbvm4/Guf51wpZhd9s6hRm5xydCo01X9R4Qz9f0dB +hfTzNM6/GkKsMMTEyjTMryByb+E4AR6DYaFxfmmFIxcEOPQ3cOpwu9EGxL8tIx6t +Bqm5H/6TKGguVxRF4PNrTT/1r1c3TQOPstBQe/bMTb5+LHkxq423xPNaS7B4jTow +pUVrpCnHXBciQiDAdZy1VW2It/ilYaUkFDGAF7aATI8BzLlZ9mBC55IH5oW9hmdm +jd2wKS4OR3qfNIRBebHEinWNq0LvwRoGisPTdE+5QqO4l5nswbF4XTUQ7wue2jg3 +8m2K8WrTxrDZD40YtSjI4akWs+433C/SqmN4uKDCr5oTGjksDbM+gFcLGazROatU +dnoLHLRFF/PP4OYpjS+hALYXAagWQ2I4A/9ZAA4gODClJKFA9x6P88G76OaxDyOk +DR4kaRUW6GfnyJxeiY7YEiCNGl91LdjGNYsb06UOGF0+D+caeOgacgux88Lma+r7 +zSPoKR2ijDCVoU5CysR6pXNl3iPXZPj8aUrBAWozObUBB66XiYpWIZ+goqjGjJGm +/w23daBF57shAFge9tmbHJDeaRiju9M4C3UgLDFxmMf+GtywEBUBJ+K5xSVF08JO +jClUlvFTOLUY8QkOWyWgRs9NhaDUtctZElHR9KqnSd5aHr0+atwPzO4LlMYqyOIY +AHO2OUTG3hCglQDmDZ9W9M8Kog== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/config/initializers/app_artifacts.rb b/config/initializers/app_artifacts.rb index ae5ecd48941..3fb55aacb11 100644 --- a/config/initializers/app_artifacts.rb +++ b/config/initializers/app_artifacts.rb @@ -2,6 +2,8 @@ AppArtifacts.setup do |store| # When adding or removing certs, make sure to update the 'saml_endpoint_configs' config + store.add_artifact(:saml_2021_cert, '/%s/saml2021.crt') + store.add_artifact(:saml_2021_key, '/%s/saml2021.key.enc') store.add_artifact(:saml_2022_cert, '/%s/saml2022.crt') store.add_artifact(:saml_2022_key, '/%s/saml2022.key.enc') store.add_artifact(:saml_2023_cert, '/%s/saml2023.crt') From f3caa7740afa9caf20a83568614fbb0b649eae8d Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 5 Apr 2023 11:25:52 -0500 Subject: [PATCH 10/25] Do not prompt for re-authentication on backup code refresh prompt (#8134) * Do not prompt for re-authentication on backup code refresh prompt changelog: Bug Fixes, Authentication, Do not prompt for re-authentication on backup code refresh prompt * fix spec --- app/controllers/users/backup_code_setup_controller.rb | 2 +- spec/controllers/users/backup_code_setup_controller_spec.rb | 2 +- spec/support/matchers/have_actions.rb | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/users/backup_code_setup_controller.rb b/app/controllers/users/backup_code_setup_controller.rb index 191ca00855d..25e2bd61bfa 100644 --- a/app/controllers/users/backup_code_setup_controller.rb +++ b/app/controllers/users/backup_code_setup_controller.rb @@ -11,7 +11,7 @@ class BackupCodeSetupController < ApplicationController before_action :set_backup_code_setup_presenter before_action :apply_secure_headers_override before_action :authorize_backup_code_disable, only: [:delete] - before_action :confirm_recently_authenticated_2fa, if: -> do + before_action :confirm_recently_authenticated_2fa, except: [:reminder], if: -> do IdentityConfig.store.reauthentication_for_second_factor_management_enabled end diff --git a/spec/controllers/users/backup_code_setup_controller_spec.rb b/spec/controllers/users/backup_code_setup_controller_spec.rb index 18c03aaaac2..6e97e92ae37 100644 --- a/spec/controllers/users/backup_code_setup_controller_spec.rb +++ b/spec/controllers/users/backup_code_setup_controller_spec.rb @@ -8,7 +8,7 @@ :authenticate_user!, :confirm_user_authenticated_for_2fa_setup, :apply_secure_headers_override, - :confirm_recently_authenticated_2fa, + [:confirm_recently_authenticated_2fa, except: ['reminder']], ) end end diff --git a/spec/support/matchers/have_actions.rb b/spec/support/matchers/have_actions.rb index 70583f08a51..cb57b06c5a0 100644 --- a/spec/support/matchers/have_actions.rb +++ b/spec/support/matchers/have_actions.rb @@ -71,9 +71,7 @@ def parsed_only_action(action) end def parsed_except_action(action) - except_option = unless_option_for(action)[0] - - "#{except_option.class}OptionParser".constantize.new(except_option).parse + unless_option_for(action)[0].instance_variable_get(:@actions).to_a end class ProcOptionParser From 67f5febd6ee89b2af837c3df742630a9d133dbf9 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 5 Apr 2023 12:15:37 -0500 Subject: [PATCH 11/25] Use Rack::Session methods when calling redis-session-store (#8132) * Use Rack::Session methods when calling redis-session-store changelog: Internal, Session Management, Use Rack::Session methods when calling redis-session-store * Update app/services/out_of_band_session_accessor.rb Co-authored-by: Zach Margolis * Update app/services/out_of_band_session_accessor.rb --------- Co-authored-by: Zach Margolis --- app/services/out_of_band_session_accessor.rb | 15 +++++++++++---- config/initializers/session_store.rb | 3 +++ spec/services/access_token_verifier_spec.rb | 8 +++++++- spec/services/id_token_builder_spec.rb | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/services/out_of_band_session_accessor.rb b/app/services/out_of_band_session_accessor.rb index f06e3c9a472..dd2146ca100 100644 --- a/app/services/out_of_band_session_accessor.rb +++ b/app/services/out_of_band_session_accessor.rb @@ -28,7 +28,7 @@ def load_x509 end def destroy - session_store.send(:destroy_session_from_sid, session_uuid, drop: true) + session_store.send(:delete_session, {}, Rack::Session::SessionId.new(session_uuid), drop: true) end # @api private @@ -60,13 +60,20 @@ def put(data, expiration = 5.minutes) 'warden.user.user.session' => data.to_h, } - session_store. - send(:set_session, {}, session_uuid, session_data, expire_after: expiration.to_i) + session_store.send( + :write_session, + {}, + Rack::Session::SessionId.new(session_uuid), + session_data, + expire_after: expiration.to_i, + ) end # @return [Hash] def session_data - @session_data ||= session_store.send(:load_session_from_redis, session_uuid) || {} + @session_data ||= session_store.send( + :find_session, {}, Rack::Session::SessionId.new(session_uuid) + ).last || {} end def session_store diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 713df6f1ae8..26a20481647 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -9,6 +9,9 @@ # cookie expires with browser close expire_after: nil, + write_fallback: true, + read_fallback: true, + # Redis expires session after N minutes ttl: IdentityConfig.store.session_timeout_in_minutes.minutes, diff --git a/spec/services/access_token_verifier_spec.rb b/spec/services/access_token_verifier_spec.rb index ed134b39bf1..9e620cc8465 100644 --- a/spec/services/access_token_verifier_spec.rb +++ b/spec/services/access_token_verifier_spec.rb @@ -7,7 +7,13 @@ subject(:verifier) { AccessTokenVerifier.new(http_authorization_header) } let(:http_authorization_header) { "Bearer #{access_token}" } - let(:identity) { build(:service_provider_identity, access_token: SecureRandom.urlsafe_base64) } + let(:identity) do + build( + :service_provider_identity, + rails_session_id: '123', + access_token: SecureRandom.urlsafe_base64, + ) + end describe '#submit' do let(:result) { verifier.submit } diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index 17b11420f10..e67521855c7 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -11,6 +11,7 @@ nonce: SecureRandom.hex, uuid: SecureRandom.uuid, ial: 2, + rails_session_id: '123', # this is a known value from an example developer guide # https://www.pingidentity.com/content/developer/en/resources/openid-connect-developers-guide.html access_token: 'dNZX1hEZ9wBCzNL40Upu646bdzQA', From e77eae25a729c1f626a2426a0908550733fc1f17 Mon Sep 17 00:00:00 2001 From: Jack Ryan Date: Wed, 5 Apr 2023 14:14:19 -0400 Subject: [PATCH 12/25] LG-9345 Maintain material consistency in IPP pages (#8104) * Change a few links that look like buttons into buttons add link role to a button that is effectively a link * changelog: User-Facing Improvements, In-person proofing, Make IPP buttons and links into what they look like * Remove left over comment * Fix JS errors, replace click_link with click_button --- .../components/in-person-call-to-action.spec.tsx | 4 ++-- .../components/in-person-call-to-action.tsx | 10 ++++++---- .../in-person-location-post-office-search-step.tsx | 2 +- .../components/in-person-prepare-step.spec.tsx | 8 ++++---- .../components/in-person-prepare-step.tsx | 6 +++--- spec/features/idv/in_person_spec.rb | 2 +- spec/support/features/in_person_helper.rb | 2 +- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/javascript/packages/document-capture/components/in-person-call-to-action.spec.tsx b/app/javascript/packages/document-capture/components/in-person-call-to-action.spec.tsx index d5e72f792a0..e5879ddf146 100644 --- a/app/javascript/packages/document-capture/components/in-person-call-to-action.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-call-to-action.spec.tsx @@ -20,8 +20,8 @@ describe('InPersonCallToAction', () => { , ); - const link = getByRole('link', { name: 'in_person_proofing.body.cta.button' }); - await userEvent.click(link); + const button = getByRole('button', { name: 'in_person_proofing.body.cta.button' }); + await userEvent.click(button); expect(trackEvent).to.have.been.calledWith( 'IdV: verify in person troubleshooting option clicked', diff --git a/app/javascript/packages/document-capture/components/in-person-call-to-action.tsx b/app/javascript/packages/document-capture/components/in-person-call-to-action.tsx index 52c242153fa..61625ca68fc 100644 --- a/app/javascript/packages/document-capture/components/in-person-call-to-action.tsx +++ b/app/javascript/packages/document-capture/components/in-person-call-to-action.tsx @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { Button } from '@18f/identity-components'; import { useInstanceId } from '@18f/identity-react-hooks'; import { t } from '@18f/identity-i18n'; +import useHistoryParam from '@18f/identity-form-steps/use-history-param'; import AnalyticsContext from '../context/analytics'; import { InPersonContext } from '../context'; @@ -15,6 +16,7 @@ function InPersonCallToAction({ altHeading, altPrompt, altButtonText }: InPerson const instanceId = useInstanceId(); const { trackEvent } = useContext(AnalyticsContext); const { inPersonCtaVariantActive } = useContext(InPersonContext); + const [, setStepName] = useHistoryParam(undefined); return (
+ onClick={() => { + setStepName('location'); trackEvent('IdV: verify in person troubleshooting option clicked', { in_person_cta_variant: inPersonCtaVariantActive, - }) - } + }); + }} > {altButtonText || t('in_person_proofing.body.cta.button')} diff --git a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx index 95e73ff07e8..f5e8fce3792 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx @@ -112,7 +112,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist address={foundAddress?.address || ''} /> )} - + ); } diff --git a/app/javascript/packages/document-capture/components/in-person-prepare-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-prepare-step.spec.tsx index 74d2cf50ef8..74c1241355d 100644 --- a/app/javascript/packages/document-capture/components/in-person-prepare-step.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-prepare-step.spec.tsx @@ -39,7 +39,7 @@ describe('InPersonPrepareStep', () => { { wrapper }, ); - await userEvent.click(getByRole('link', { name: 'forms.buttons.continue' })); + await userEvent.click(getByRole('button', { name: 'forms.buttons.continue' })); await waitFor(() => window.location.hash === inPersonURL); expect(trackEvent).to.have.been.calledWith('IdV: prepare submitted'); @@ -60,10 +60,10 @@ describe('InPersonPrepareStep', () => { { wrapper }, ); - const link = getByRole('link', { name: 'forms.buttons.continue' }); + const button = getByRole('button', { name: 'forms.buttons.continue' }); - const didFollowLinkOnFirstClick = fireEvent.click(link); - const didFollowLinkOnSecondClick = fireEvent.click(link); + const didFollowLinkOnFirstClick = fireEvent.click(button); + const didFollowLinkOnSecondClick = fireEvent.click(button); clock.tick(delay); diff --git a/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx b/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx index 91f5bbcf829..db61517b3b7 100644 --- a/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx +++ b/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx @@ -29,7 +29,7 @@ function InPersonPrepareStep({ toPreviousStep, value }) { setIsSubmitting(true); removeUnloadProtection(); await trackEvent('IdV: prepare submitted'); - window.location.href = (event.target as HTMLAnchorElement).href; + window.location.href = inPersonURL!; } }; @@ -59,7 +59,7 @@ function InPersonPrepareStep({ toPreviousStep, value }) { {flowPath === 'hybrid' && } {inPersonURL && flowPath === 'standard' && (
- + {t('forms.buttons.continue')}
@@ -78,7 +78,7 @@ function InPersonPrepareStep({ toPreviousStep, value }) { )}

- + ); } diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb index 6cfb066151f..dd8d37d95c7 100644 --- a/spec/features/idv/in_person_spec.rb +++ b/spec/features/idv/in_person_spec.rb @@ -342,7 +342,7 @@ mock_doc_auth_attention_with_barcode attach_and_submit_images - click_link t('in_person_proofing.body.cta.button') + click_button t('in_person_proofing.body.cta.button') search_for_post_office within page.first('.location-collection-item') do click_spinner_button_and_wait t('in_person_proofing.body.location.location_button') diff --git a/spec/support/features/in_person_helper.rb b/spec/support/features/in_person_helper.rb index fa52c358d19..5711c3c0e63 100644 --- a/spec/support/features/in_person_helper.rb +++ b/spec/support/features/in_person_helper.rb @@ -73,7 +73,7 @@ def begin_in_person_proofing(_user = nil) complete_doc_auth_steps_before_document_capture_step mock_doc_auth_attention_with_barcode attach_and_submit_images - click_link t('in_person_proofing.body.cta.button') + click_button t('in_person_proofing.body.cta.button') end def search_for_post_office From 3fddca341723aa34434873e0104df832458f6fe7 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Wed, 5 Apr 2023 12:50:54 -0700 Subject: [PATCH 13/25] Ignore unhandled HTTP Verb errors (#8139) changelog: Internal, Error, Update list of ignored errors --- config/newrelic.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/newrelic.yml b/config/newrelic.yml index 901f693003e..08269ea96e0 100644 --- a/config/newrelic.yml +++ b/config/newrelic.yml @@ -25,6 +25,7 @@ production: ActionController::BadRequest ActionController::ParameterMissing ActionController::RoutingError + ActionController::UnknownHttpMethod ActionDispatch::Http::MimeNegotiation::InvalidType ActionDispatch::Http::Parameters::ParseError GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError From 662b4d60c0b3ddd7217b4f6d3a860fdcdb746748 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 5 Apr 2023 16:21:19 -0400 Subject: [PATCH 14/25] LG-9411 fraud review script fix (#8141) * add fraud_review_eligible? to user model review_eligible? was moved from the proofing component to the user model. Instead of checking the proofing_component's verified_at field we will check the user's profile to determine if they are within the 30 day time frame * change user factory to use new verified_at * remove old review eligible from proofing component * add changelog changelog: Internal, IdV Fraud, Place fraud_review_eligible in user model * add guard for fraud_review_eligible --- app/models/proofing_component.rb | 4 ---- app/models/user.rb | 5 +++++ lib/tasks/review_profile.rake | 4 ++-- spec/factories/users.rb | 3 +-- spec/models/proofing_component_spec.rb | 27 ------------------------ spec/models/user_spec.rb | 29 ++++++++++++++++++++++++++ 6 files changed, 37 insertions(+), 35 deletions(-) delete mode 100644 spec/models/proofing_component_spec.rb diff --git a/app/models/proofing_component.rb b/app/models/proofing_component.rb index 0c6c38a5e55..011ca97381a 100644 --- a/app/models/proofing_component.rb +++ b/app/models/proofing_component.rb @@ -1,7 +1,3 @@ class ProofingComponent < ApplicationRecord belongs_to :user - - def review_eligible? - verified_at&.after?(30.days.ago) - end end diff --git a/app/models/user.rb b/app/models/user.rb index ecf8b1dff0c..021728c8381 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -102,6 +102,11 @@ def pending_profile profiles.gpo_verification_pending.order(created_at: :desc).first end + def fraud_review_eligible? + return false if !fraud_review_pending? + fraud_review_pending_profile.verified_at&.after?(30.days.ago) + end + def fraud_review_pending? fraud_review_pending_profile.present? end diff --git a/lib/tasks/review_profile.rake b/lib/tasks/review_profile.rake index 86c1c85a9e2..a448b0917b3 100644 --- a/lib/tasks/review_profile.rake +++ b/lib/tasks/review_profile.rake @@ -26,7 +26,7 @@ namespace :users do next end - if user.proofing_component.review_eligible? + if 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.proofing_component.review_eligible? + if user.fraud_review_eligible? profile = user.fraud_review_pending_profile profile.reject_for_fraud(notify_user: true) diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 6313cbd9dd3..09a6e8a671a 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -214,8 +214,7 @@ signed_up after :build do |user| - create(:profile, :fraud_review_pending, :with_pii, user: user) - create(:proofing_component, :eligible_for_review, user: user) + create(:profile, :fraud_review_pending, :verified, :with_pii, user: user) end end diff --git a/spec/models/proofing_component_spec.rb b/spec/models/proofing_component_spec.rb deleted file mode 100644 index f0ba6887f40..00000000000 --- a/spec/models/proofing_component_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'rails_helper' - -RSpec.describe ProofingComponent do - describe '#review_eligible?' do - subject(:review_eligible?) do - build(:proofing_component, verified_at: verified_at).review_eligible? - end - - context 'when verified_at is nil' do - let(:verified_at) { nil } - - it { is_expected.to be_falsey } - end - - context 'when verified_at is within 30 days' do - let(:verified_at) { 15.days.ago } - - it { is_expected.to be_truthy } - end - - context 'when verified_at is older than 30 days' do - let(:verified_at) { 45.days.ago } - - it { is_expected.to be_falsey } - end - end -end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0d826f73dce..888de6f0ca0 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -510,6 +510,35 @@ end end + describe '#fraud_review_eligible?' do + context 'when verified_at is nil' do + it 'returns false' do + user = User.new + create(:profile, user: user, fraud_review_pending: true, verified_at: nil) + + expect(user.fraud_review_eligible?).to be_falsey + end + end + + context 'when verified_at is within 30 days' do + it 'returns true' do + user = User.new + create(:profile, user: user, fraud_review_pending: true, verified_at: 15.days.ago) + + expect(user.fraud_review_eligible?).to eq true + end + end + + context 'when verified_at is older than 30 days' do + it 'returns false' do + user = User.new + create(:profile, user: user, fraud_review_pending: true, verified_at: 45.days.ago) + + 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 = User.new From 27e767c3584f4e0baa2b5840e65eef6e57cddd87 Mon Sep 17 00:00:00 2001 From: Sonia Connolly Date: Wed, 5 Apr 2023 13:38:03 -0700 Subject: [PATCH 15/25] Remove DocumentCaptureController 404 before action (#8128) Prepare for turning on DocumentCaptureController feature flag. [skip changelog] --- .../idv/document_capture_controller.rb | 5 ----- .../idv/document_capture_controller_spec.rb | 20 ------------------- 2 files changed, 25 deletions(-) diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 93c9f45a4c9..bdebd9e710f 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -6,7 +6,6 @@ class DocumentCaptureController < ApplicationController include StepUtilitiesConcern include DocumentCaptureConcern - before_action :render_404_if_document_capture_controller_disabled before_action :confirm_two_factor_authenticated before_action :confirm_upload_step_complete before_action :confirm_document_capture_needed @@ -59,10 +58,6 @@ def extra_view_variables private - def render_404_if_document_capture_controller_disabled - render_not_found unless IdentityConfig.store.doc_auth_document_capture_controller_enabled - end - def confirm_upload_step_complete return if flow_session['Idv::Steps::UploadStep'] diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index b757586cfa1..8eb45995dc0 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -28,13 +28,6 @@ end describe 'before_actions' do - it 'checks that feature flag is enabled' do - expect(subject).to have_actions( - :before, - :render_404_if_document_capture_controller_disabled, - ) - end - it 'includes authentication before_action' do expect(subject).to have_actions( :before, @@ -154,17 +147,4 @@ end end end - - context 'when doc_auth_document_capture_controller_enabled is false' do - before do - allow(IdentityConfig.store).to receive(:doc_auth_document_capture_controller_enabled). - and_return(false) - end - - it 'returns 404' do - get :show - - expect(response.status).to eq(404) - end - end end From ecfdbaee6a0707b2db06a2a5e80745eca6709e7b Mon Sep 17 00:00:00 2001 From: Matt Hinz Date: Wed, 5 Apr 2023 13:41:46 -0700 Subject: [PATCH 16/25] LG-8710: New IDV unavailable screen (#8106) * Add idv_available config && feature Tie feature to vendor availability as well as config. * Add "IDV Unavailable" view * Add Idv::UnavailableController * Redirect to IDV unavailable page from reg When incoming request requires identity verification, but identity verification is not available, error out early rather than forcing user to create an account first. * Remove unneeded redirect from DocAuthController If IDV is unavailable, this controller will not get called (see routes.rb) * Refactor OutageStatus - We don't actually pass any args in the constructor anymore, so remove them - Remove code paths that rely on values set by those constructor args - Remove unused translations * Update outage feature spec When unavailable, routes inside IDV will not have their path change, but _will_ display an error message. * changelog: User-Facing Improvements, Identity verification, Provide a more helpful and detailed error message when identity verification is unavailable. * Update spec/controllers/idv/unavailable_controller_spec.rb Co-authored-by: Sonia Connolly * Update spec/controllers/idv/unavailable_controller_spec.rb Co-authored-by: Sonia Connolly * Update spec/controllers/idv/unavailable_controller_spec.rb Co-authored-by: Sonia Connolly * Adjust order of args to AnalyticsEvents::vendor_outage * Don't call OutageStatus.new twice * Remove tests for partial_outage support We don't do this anymore. * Move status page link into new paragraph work around weird braking associated with external link's `display: inline-block` in USWDS 2 --------- Co-authored-by: Sonia Connolly --- app/controllers/idv/doc_auth_controller.rb | 8 -- app/controllers/idv/unavailable_controller.rb | 34 ++++++ .../sign_up/registrations_controller.rb | 11 +- app/controllers/vendor_outage_controller.rb | 11 +- app/services/analytics_events.rb | 4 +- app/services/outage_status.rb | 31 +---- app/views/idv/unavailable/show.html.erb | 42 +++++++ config/application.yml.default | 1 + config/locales/idv/en.yml | 12 ++ config/locales/idv/es.yml | 13 +++ config/locales/idv/fr.yml | 14 +++ config/locales/vendor_outage/en.yml | 10 -- config/locales/vendor_outage/es.yml | 13 --- config/locales/vendor_outage/fr.yml | 11 -- config/routes.rb | 7 ++ lib/feature_management.rb | 5 + lib/identity_config.rb | 1 + .../idv/unavailable_controller_spec.rb | 82 +++++++++++++ .../sign_up/registrations_controller_spec.rb | 13 +++ spec/features/idv/outage_spec.rb | 110 ++++++++++-------- spec/lib/feature_management_spec.rb | 36 ++++++ spec/services/outage_status_spec.rb | 49 +------- .../idv/unavailable/show.html.erb_spec.rb | 52 +++++++++ 23 files changed, 393 insertions(+), 177 deletions(-) create mode 100644 app/controllers/idv/unavailable_controller.rb create mode 100644 app/views/idv/unavailable/show.html.erb create mode 100644 spec/controllers/idv/unavailable_controller_spec.rb create mode 100644 spec/views/idv/unavailable/show.html.erb_spec.rb diff --git a/app/controllers/idv/doc_auth_controller.rb b/app/controllers/idv/doc_auth_controller.rb index b7ac16c4417..a26e6431bf0 100644 --- a/app/controllers/idv/doc_auth_controller.rb +++ b/app/controllers/idv/doc_auth_controller.rb @@ -72,17 +72,9 @@ def flow_session def check_for_outage return if flow_session[:skip_vendor_outage] - return redirect_for_proofing_vendor_outage if OutageStatus.new.any_idv_vendor_outage? return redirect_for_gpo_only if FeatureManagement.idv_gpo_only? end - def redirect_for_proofing_vendor_outage - session[:vendor_outage_redirect] = current_step - session[:vendor_outage_redirect_from_idv] = true - - redirect_to vendor_outage_url - end - def redirect_for_gpo_only return redirect_to vendor_outage_url unless FeatureManagement.gpo_verification_enabled? diff --git a/app/controllers/idv/unavailable_controller.rb b/app/controllers/idv/unavailable_controller.rb new file mode 100644 index 00000000000..79ffe2bf1e4 --- /dev/null +++ b/app/controllers/idv/unavailable_controller.rb @@ -0,0 +1,34 @@ +module Idv + class UnavailableController < ApplicationController + ALLOWED_FROM_LOCATIONS = [SignUp::RegistrationsController::CREATE_ACCOUNT] + + before_action :redirect_if_idv_available_and_from_create_account + + def show + analytics.vendor_outage( + vendor_status: { + acuant: IdentityConfig.store.vendor_status_acuant, + lexisnexis_instant_verify: IdentityConfig.store.vendor_status_lexisnexis_instant_verify, + lexisnexis_trueid: IdentityConfig.store.vendor_status_lexisnexis_trueid, + sms: IdentityConfig.store.vendor_status_sms, + voice: IdentityConfig.store.vendor_status_voice, + }, + redirect_from: from, + ) + end + + private + + def from + params[:from] if ALLOWED_FROM_LOCATIONS.include?(params[:from]) + end + + def from_create_account? + from == SignUp::RegistrationsController::CREATE_ACCOUNT + end + + def redirect_if_idv_available_and_from_create_account + redirect_to sign_up_email_url if FeatureManagement.idv_available? && from_create_account? + end + end +end diff --git a/app/controllers/sign_up/registrations_controller.rb b/app/controllers/sign_up/registrations_controller.rb index 51a9ddb7e71..0f26e3fb160 100644 --- a/app/controllers/sign_up/registrations_controller.rb +++ b/app/controllers/sign_up/registrations_controller.rb @@ -5,7 +5,7 @@ class RegistrationsController < ApplicationController before_action :confirm_two_factor_authenticated, only: [:destroy_confirm] before_action :require_no_authentication - before_action :redirect_if_ial2_and_vendor_outage + before_action :redirect_if_ial2_and_idv_unavailable CREATE_ACCOUNT = 'create_account' @@ -71,11 +71,10 @@ def sp_request_id ServiceProviderRequestProxy.from_uuid(request_id).uuid end - def redirect_if_ial2_and_vendor_outage - return unless ial2_requested? && OutageStatus.new.any_idv_vendor_outage? - - session[:vendor_outage_redirect] = CREATE_ACCOUNT - return redirect_to vendor_outage_url + def redirect_if_ial2_and_idv_unavailable + if ial2_requested? && !FeatureManagement.idv_available? + redirect_to idv_unavailable_path(from: CREATE_ACCOUNT) + end end end end diff --git a/app/controllers/vendor_outage_controller.rb b/app/controllers/vendor_outage_controller.rb index fdc023b203f..cf353672a33 100644 --- a/app/controllers/vendor_outage_controller.rb +++ b/app/controllers/vendor_outage_controller.rb @@ -1,13 +1,10 @@ class VendorOutageController < ApplicationController def show - vendor_status = OutageStatus.new( - sp: current_sp, - from: session.delete(:vendor_outage_redirect), - from_idv: session.delete(:vendor_outage_redirect_from_idv), - ) - @specific_message = vendor_status.outage_message + outage_status = OutageStatus.new + + @specific_message = outage_status.outage_message @show_gpo_option = from_idv_phone? && gpo_letter_available? - vendor_status.track_event(analytics) + outage_status.track_event(analytics) end private diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index a8d551a5151..512937553cf 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -2909,12 +2909,12 @@ def user_registration_2fa_setup_visit track_event('User Registration: 2FA Setup visited') end - # @param [String] redirect_from # @param [Hash] vendor_status + # @param [String,nil] redirect_from # Tracks when vendor has outage def vendor_outage( - redirect_from:, vendor_status:, + redirect_from: nil, **extra ) track_event( diff --git a/app/services/outage_status.rb b/app/services/outage_status.rb index 290dbde4acc..bb7b532d279 100644 --- a/app/services/outage_status.rb +++ b/app/services/outage_status.rb @@ -1,12 +1,6 @@ class OutageStatus include ActionView::Helpers::TranslationHelper - def initialize(from: nil, from_idv: nil, sp: nil) - @from = from - @from_idv = from_idv - @sp = sp - end - IDV_VENDORS = %i[acuant lexisnexis_instant_verify lexisnexis_trueid].freeze PHONE_VENDORS = %i[sms voice].freeze ALL_VENDORS = (IDV_VENDORS + PHONE_VENDORS).freeze @@ -55,31 +49,15 @@ def phone_finder_outage? all_vendor_outage?([:lexisnexis_phone_finder]) end - def from_idv? - from_idv - end - # Returns an appropriate error message based upon the type of outage or what the user was doing # when they encountered the outage. # # @return [String, nil] the localized message. def outage_message if any_idv_vendor_outage? - if from_idv? - if sp - t('vendor_outage.blocked.idv.with_sp', service_provider: sp.friendly_name) - else - t('vendor_outage.blocked.idv.without_sp') - end - else - t('vendor_outage.blocked.idv.generic') - end + t('vendor_outage.blocked.idv.generic') elsif any_phone_vendor_outage? - if from_idv? - t('vendor_outage.blocked.phone.idv') - else - t('vendor_outage.blocked.phone.default') - end + t('vendor_outage.blocked.phone.default') end end @@ -94,11 +72,6 @@ def track_event(analytics) sms: IdentityConfig.store.vendor_status_sms, voice: IdentityConfig.store.vendor_status_voice, }, - redirect_from: from, ) end - - private - - attr_reader :from, :from_idv, :sp end diff --git a/app/views/idv/unavailable/show.html.erb b/app/views/idv/unavailable/show.html.erb new file mode 100644 index 00000000000..6a2f8c8bade --- /dev/null +++ b/app/views/idv/unavailable/show.html.erb @@ -0,0 +1,42 @@ +<% title t('idv.titles.unavailable') %> + +<%= render StatusPageComponent.new(status: :error) do |c| %> + + <% c.header { t('idv.titles.unavailable') } %> + +

+ <% if decorated_session.sp_name.present? %> + <%= t('idv.unavailable.idv_explanation.with_sp_html', sp: decorated_session.sp_name) %> + <% else %> + <%= t('idv.unavailable.idv_explanation.without_sp') %> + <% end %> +

+ +

+ <%= t('idv.unavailable.technical_difficulties') %> +

+ +

+ <%= t( + 'idv.unavailable.next_steps_html', + app_name: APP_NAME, + status_page_link: new_window_link_to( + t('idv.unavailable.status_page_link'), + StatusPage.base_url, + ), + ) %> +

+ + <% c.action_button( + action: ->(**tag_options, &block) do + link_to( + return_to_sp_failure_to_proof_path(location: :unavailable), + **tag_options, + &block + ) + end, + big: true, + wide: true, + ).with_content(t('idv.unavailable.exit_button', app_name: APP_NAME)) %> + +<% end %> \ No newline at end of file diff --git a/config/application.yml.default b/config/application.yml.default index 3b67dda31c3..dc0d86c0267 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -120,6 +120,7 @@ hide_phone_mfa_signup: false identity_pki_disabled: false identity_pki_local_dev: false idv_attempt_window_in_hours: 6 +idv_available: true idv_contact_phone_number: (844) 555-5555 idv_max_attempts: 5 idv_min_age_years: 13 diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index 827fed84495..5154bc18e20 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -225,6 +225,7 @@ en: session: phone: Enter your phone number review: Re-enter your %{app_name} password to protect your data + unavailable: 'We are working to resolve an error' troubleshooting: headings: missing_required_items: Are you missing one of these items? @@ -241,6 +242,17 @@ en: learn_more_verify_in_person: Learn more about verifying in person supported_documents: See a list of accepted state-issued IDs verify_by_mail: Verify your address by mail instead + unavailable: + exit_button: 'Exit %{app_name}' + idv_explanation: + with_sp_html: '%{sp} needs to make sure you are you — not + someone pretending to be you.' + without_sp: 'The agency that you are trying to access needs to make sure you are + you — not someone pretending to be you.' + next_steps_html: '%{status_page_link} or exit %{app_name} and try again later.' + status_page_link: 'Get updates on our status page' + technical_difficulties: Unfortunately, we are having technical difficulties and + cannot verify your identity at this time. welcome: no_js_header: You must enable JavaScript to verify your identity. no_js_intro: '%{sp_name} needs you to verify your identity. You need to enable diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index c93d8cbf53e..7514b54df91 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -240,6 +240,7 @@ es: session: phone: Introduzca su número de teléfono review: Vuelve a ingresar tu contraseña de %{app_name} para encriptar tus datos + unavailable: Estamos trabajando para resolver un error troubleshooting: headings: missing_required_items: '¿Le falta alguno de estos puntos?' @@ -258,6 +259,18 @@ es: supported_documents: Vea la lista de documentos de identidad emitidos por el estado que son aceptados verify_by_mail: Verifique su dirección por correo + unavailable: + exit_button: 'Salir de %{app_name}' + idv_explanation: + with_sp_html: '%{sp} necesita asegurarse de que es usted + realmente y no alguien que se hace pasar por usted.' + without_sp: 'La agencia a la que está intentando acceder debe asegurarse de que + usted sea quien dice ser, y no alguien que se hace pasar por usted.' + next_steps_html: '%{status_page_link} o salga de %{app_name} y vuelva a + intentarlo más tarde.' + status_page_link: 'Consulte las actualizaciones en nuestra página de estado' + technical_difficulties: Lamentablemente, debido a problemas técnicos por nuestra + parte, tal vez no podamos verificar su identidad en estos momentos. welcome: no_js_header: Debe habilitar JavaScript para verificar su identidad. no_js_intro: '%{sp_name} requiere que usted verifique su identidad. Debe diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml index 19f128afde7..bb94a64a887 100644 --- a/config/locales/idv/fr.yml +++ b/config/locales/idv/fr.yml @@ -254,6 +254,7 @@ fr: session: phone: Entrez votre numéro de téléphone review: Entrez à nouveau votre mot de passe %{app_name} pour crypter vos données + unavailable: Nous travaillons à la résolution d’une erreur troubleshooting: headings: missing_required_items: Est-ce qu’il vous manque un de ces éléments? @@ -272,6 +273,19 @@ fr: learn_more_verify_in_person: En savoir plus sur la vérification en personne supported_documents: Voir la liste des pièces d’identité acceptées et délivrées par l’État verify_by_mail: Vérifiez plutôt votre adresse par courrier + unavailable: + exit_button: 'Quitter %{app_name}' + idv_explanation: + with_sp_html: '%{sp} doit s’assurer que c’est bien vous — et + non quelqu’un qui se fait passer pour vous.' + without_sp: 'L’agence à laquelle vous essayez d’accéder doit s’assurer qu’il + s’agit bien de vous, et non de quelqu’un qui se fait passer pour + vous.' + next_steps_html: '%{status_page_link} ou quittez le site %{app_name} et + réessayez plus tard.' + status_page_link: 'Obtenez des mises à jour sur notre page de statut' + technical_difficulties: Malheureusement, nous rencontrons des difficultés + techniques et ne pouvons pas vérifier votre identité pour le moment. welcome: no_js_header: Vous devez activer JavaScript pour vérifier votre identité. no_js_intro: '%{sp_name} a besoin de vous pour vérifier votre identité. Vous diff --git a/config/locales/vendor_outage/en.yml b/config/locales/vendor_outage/en.yml index a7d9e25cdfb..25a279c3b04 100644 --- a/config/locales/vendor_outage/en.yml +++ b/config/locales/vendor_outage/en.yml @@ -36,18 +36,8 @@ en: idv: generic: We are having technical difficulties on our end and cannot verify your identity at this time. Please try again later. - with_sp: '%{service_provider} needs to make sure you are you — not someone - pretending to be you. Unfortunately, we are having technical - difficulties and cannot verify your identity at this time. Please try - again later.' - without_sp: The agency that you are trying to access needs to make sure you are - you — not someone pretending to be you. Unfortunately, we are having - technical difficulties and cannot verify your identity at this time. - Please try again later. phone: default: We cannot verify phones at this time. Please try again later. - idv: We cannot verify phones at this time. Please try again later or verify your - address by mail instead. get_updates: Get updates get_updates_on_status_page: Get updates on our status page working: We are working to resolve an error diff --git a/config/locales/vendor_outage/es.yml b/config/locales/vendor_outage/es.yml index 5533007f109..78d833924d9 100644 --- a/config/locales/vendor_outage/es.yml +++ b/config/locales/vendor_outage/es.yml @@ -42,22 +42,9 @@ es: generic: Debido a problemas técnicos por nuestra parte, no podemos verificar su identidad en estos momentos. Por favor, inténtelo nuevamente más tarde. - with_sp: '%{service_provider} necesita asegurarse de que es usted realmente y no - alguien que se hace pasar por usted. Lamentablemente, debido a - problemas técnicos por nuestra parte, tal vez no podamos verificar su - identidad en estos momentos. Por favor, inténtelo nuevamente más - tarde.' - without_sp: La agencia a la que está intentando acceder debe asegurarse de que - usted sea quien dice ser, y no alguien que se hace pasar por usted. - Lamentablemente, debido a problemas técnicos por nuestra parte, tal - vez no podamos verificar su identidad en estos momentos. Por favor, - inténtelo nuevamente más tarde. phone: default: No podemos verificar teléfonos en estos momentos. Por favor, inténtelo nuevamente más tarde. - idv: No podemos verificar teléfonos en estos momentos. Por favor, inténtelo - nuevamente más tarde o, en lugar de ello, verifique su dirección por - correo. get_updates: Obtenga actualizaciones get_updates_on_status_page: Reciba actualizaciones en nuestra página de estado working: Estamos trabajando para corregir un error diff --git a/config/locales/vendor_outage/fr.yml b/config/locales/vendor_outage/fr.yml index 7c820ef82fd..de657734133 100644 --- a/config/locales/vendor_outage/fr.yml +++ b/config/locales/vendor_outage/fr.yml @@ -40,20 +40,9 @@ fr: idv: generic: Nous rencontrons des difficultés techniques et ne pouvons pas vérifier votre identité pour le moment. Veuillez réessayer plus tard. - with_sp: '%{service_provider} doit s’assurer que c’est bien vous — et non - quelqu’un qui se fait passer pour vous. Malheureusement, nous - rencontrons des difficultés techniques et ne pouvons pas vérifier - votre identité pour le moment. Veuillez réessayer plus tard.' - without_sp: L’agence à laquelle vous essayez d’accéder doit s’assurer qu’il - s’agit bien de vous, et non de quelqu’un qui se fait passer pour vous. - Malheureusement, nous rencontrons des difficultés techniques et ne - pouvons pas vérifier votre identité pour le moment. Veuillez réessayer - plus tard. phone: default: Nous ne pouvons pas vérifier les téléphones pour le moment. Veuillez réessayer plus tard. - idv: Nous ne pouvons pas vérifier les téléphones pour le moment. Veuillez - réessayer plus tard ou vérifier votre adresse par la poste. get_updates: Obtenir des mises à jour get_updates_on_status_page: Obtenez des mises à jour sur notre page de statut working: Nous travaillons à la résolution d’une erreur diff --git a/config/routes.rb b/config/routes.rb index f50486b1f35..332c7a0d92b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -305,11 +305,18 @@ get '/restricted' => 'banned_user#show', as: :banned_user + get '/errors/idv_unavailable' => 'idv/unavailable#show', as: :idv_unavailable + scope '/verify', as: 'idv' do get '/' => 'idv#index' get '/activated' => 'idv#activated' end scope '/verify', module: 'idv', as: 'idv' do + if !FeatureManagement.idv_available? + # IdV has been disabled. + match '/*path' => 'unavailable#show', via: %i[get post] + end + get '/mail_only_warning' => 'gpo_only_warning#show' get '/come_back_later' => 'come_back_later#show' get '/personal_key' => 'personal_key#show' diff --git a/lib/feature_management.rb b/lib/feature_management.rb index 4ab08c5fe96..3aeb8c86296 100644 --- a/lib/feature_management.rb +++ b/lib/feature_management.rb @@ -15,6 +15,11 @@ def self.identity_pki_disabled? !IdentityConfig.store.piv_cac_verify_token_url end + def self.idv_available? + return false if !IdentityConfig.store.idv_available + !OutageStatus.new.any_idv_vendor_outage? + end + def self.development_and_identity_pki_disabled? # This controls if we try to hop over to identity-pki or just throw up # a screen asking for a Subject or one of a list of error conditions. diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 0be4b47faef..c25562d9e9d 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -199,6 +199,7 @@ def self.build_store(config_map) config.add(:identity_pki_disabled, type: :boolean) config.add(:identity_pki_local_dev, type: :boolean) config.add(:idv_attempt_window_in_hours, type: :integer) + config.add(:idv_available, type: :boolean) config.add(:idv_contact_phone_number, type: :string) config.add(:idv_max_attempts, type: :integer) config.add(:idv_min_age_years, type: :integer) diff --git a/spec/controllers/idv/unavailable_controller_spec.rb b/spec/controllers/idv/unavailable_controller_spec.rb new file mode 100644 index 00000000000..5c868d577be --- /dev/null +++ b/spec/controllers/idv/unavailable_controller_spec.rb @@ -0,0 +1,82 @@ +require 'rails_helper' + +describe Idv::UnavailableController, type: :controller do + let(:idv_available) { false } + + before do + allow(IdentityConfig.store).to receive(:idv_available).and_return(idv_available) + end + + describe '#show' do + let(:params) { nil } + + before do + stub_analytics + get :show, params: params + end + + it 'returns 200 OK' do + # https://http.cat/200 + expect(response.status).to eql(200) + end + + it 'logs an analytics event with redirect_from nil' do + expect(@analytics).to have_logged_event( + 'Vendor Outage', + redirect_from: nil, + vendor_status: { + acuant: :operational, + lexisnexis_instant_verify: :operational, + lexisnexis_trueid: :operational, + sms: :operational, + voice: :operational, + }, + ) + end + + it 'renders the view' do + expect(response).to render_template('idv/unavailable/show') + end + + context 'coming from the create account page' do + let(:params) do + { from: SignUp::RegistrationsController::CREATE_ACCOUNT } + end + it 'logs an analytics event with redirect_from CREATE_ACCOUNT' do + expect(@analytics).to have_logged_event( + 'Vendor Outage', + redirect_from: SignUp::RegistrationsController::CREATE_ACCOUNT, + vendor_status: { + acuant: :operational, + lexisnexis_instant_verify: :operational, + lexisnexis_trueid: :operational, + sms: :operational, + voice: :operational, + }, + ) + end + it 'renders the view' do + expect(response).to render_template('idv/unavailable/show') + end + end + + context 'IdV is enabled' do + let(:idv_available) { true } + + it 'renders the view when from: is nil' do + expect(response).to render_template('idv/unavailable/show') + end + + it 'returns a 200' do + expect(response.status).to eql(200) + end + + context 'coming from the create account page' do + let(:params) { { from: SignUp::RegistrationsController::CREATE_ACCOUNT } } + it 'redirects back to create account' do + expect(response).to redirect_to(sign_up_email_path) + end + end + end + end +end diff --git a/spec/controllers/sign_up/registrations_controller_spec.rb b/spec/controllers/sign_up/registrations_controller_spec.rb index 4d9964e8bdc..6f3511ad465 100644 --- a/spec/controllers/sign_up/registrations_controller_spec.rb +++ b/spec/controllers/sign_up/registrations_controller_spec.rb @@ -25,6 +25,19 @@ expect { get :new }. to raise_error(Mime::Type::InvalidMimeType) end + + context 'IdV unavailable' do + before do + allow(IdentityConfig.store).to receive(:idv_available).and_return(false) + end + it 'redirects to idv vendor outage page when ial2 requested' do + allow(controller).to receive(:ial2_requested?).and_return(true) + get :new + expect(response).to redirect_to( + idv_unavailable_path(from: SignUp::RegistrationsController::CREATE_ACCOUNT), + ) + end + end end describe '#create' do diff --git a/spec/features/idv/outage_spec.rb b/spec/features/idv/outage_spec.rb index e52db17f486..5b221e7ad31 100644 --- a/spec/features/idv/outage_spec.rb +++ b/spec/features/idv/outage_spec.rb @@ -20,12 +20,54 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) let(:new_password) { 'some really awesome new password' } let(:pii) { { ssn: '666-66-1234', dob: '1920-01-01', first_name: 'alice' } } - context 'vendor_status_lexisnexis_phone_finder set to full_outage', js: true do - before do - allow(IdentityConfig.store).to receive(:vendor_status_lexisnexis_phone_finder). - and_return(:full_outage) + let(:vendor_status_acuant) { :operational } + let(:vendor_status_lexisnexis_instant_verify) { :operational } + let(:vendor_status_lexisnexis_phone_finder) { :operational } + let(:vendor_status_lexisnexis_trueid) { :operational } + let(:vendor_status_sms) { :operational } + let(:vendor_status_voice) { :operational } + + let(:enable_usps_verification) { true } + let(:feature_idv_force_gpo_verification_enabled) { false } + let(:feature_idv_hybrid_flow_enabled) { true } + + before do + # Wire up various let()s to configuration keys + %w[ + acuant + lexisnexis_instant_verify + lexisnexis_phone_finder + lexisnexis_trueid + sms + voice + ].each do |service| + vendor_status_key = "vendor_status_#{service}".to_sym + allow(IdentityConfig.store).to receive(vendor_status_key). + and_return(send(vendor_status_key)) + end + + %w[ + enable_usps_verification + feature_idv_force_gpo_verification_enabled + feature_idv_hybrid_flow_enabled + ].each do |key| + allow(IdentityConfig.store).to receive(key). + and_return(send(key)) end + # Configuration / vendor status changes can effect Rails routing tables. + # Force routes to be reloaded when we've modified configuration. + Rails.application.reload_routes! + end + + after do + # Don't leave stale routes sitting around. + Rails.application.reload_routes! + end + + context 'vendor_status_lexisnexis_phone_finder set to full_outage', js: true do + let(:vendor_status_lexisnexis_phone_finder) { :full_outage } + it 'takes the user through the mail only flow, allowing hybrid' do sign_in_with_idv_required(user: user) @@ -51,10 +93,7 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) end context 'GPO only enabled, but user starts over', js: true do - before do - allow(IdentityConfig.store).to receive(:feature_idv_force_gpo_verification_enabled). - and_return(true) - end + let(:feature_idv_force_gpo_verification_enabled) { true } it 'shows mail only warning page before idv welcome page' do sign_in_with_idv_required(user: user) @@ -70,14 +109,7 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) end context 'force GPO only without phone outages', js: true do - before do - allow(IdentityConfig.store).to receive(:feature_idv_force_gpo_verification_enabled). - and_return(true) - allow(IdentityConfig.store).to receive(:vendor_status_sms). - and_return(:operational) - allow(IdentityConfig.store).to receive(:vendor_status_voice). - and_return(:operational) - end + let(:feature_idv_force_gpo_verification_enabled) { true } it 'shows mail only warning page before idv welcome page' do sign_in_with_idv_required(user: user) @@ -91,12 +123,8 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) end context 'force GPO only, but GPO not enabled', js: true do - before do - allow(IdentityConfig.store).to receive(:feature_idv_force_gpo_verification_enabled). - and_return(true) - allow(IdentityConfig.store).to receive(:enable_usps_verification). - and_return(false) - end + let(:feature_idv_force_gpo_verification_enabled) { true } + let(:enable_usps_verification) { false } it 'shows mail only warning page before idv welcome page' do sign_in_with_idv_required(user: user) @@ -110,10 +138,7 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) %i[vendor_status_sms vendor_status_voice].each do |flag| context "#{flag} set to full_outage" do - before do - allow(IdentityConfig.store).to receive(flag). - and_return(:full_outage) - end + let(flag) { :full_outage } it 'shows mail only warning page before idv welcome page' do sign_in_with_idv_required(user: user, sms_or_totp: :totp) @@ -146,13 +171,9 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) end context 'feature_idv_force_gpo_verification_enabled set to true', js: true do + let(:feature_idv_force_gpo_verification_enabled) { true } let(:user) { user_with_2fa } - before do - allow(IdentityConfig.store).to receive(:feature_idv_force_gpo_verification_enabled). - and_return(true) - end - it 'shows mail only warning page before idv welcome page', js: true do sign_in_with_idv_required(user: user, sms_or_totp: :sms) @@ -175,11 +196,7 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) context 'feature_idv_hybrid_flow_enabled set to false', js: true do let(:user) { user_with_2fa } - - before do - allow(IdentityConfig.store).to receive(:feature_idv_hybrid_flow_enabled). - and_return(false) - end + let(:feature_idv_hybrid_flow_enabled) { false } it 'does not show the mail only warning page before idv welcome page' do sign_in_with_idv_required(user: user, sms_or_totp: :sms) @@ -200,16 +217,12 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) %w[acuant lexisnexis_instant_verify lexisnexis_trueid].each do |service| context "vendor_status_#{service} set to full_outage" do let(:user) { user_with_2fa } - before do - allow(IdentityConfig.store).to receive("vendor_status_#{service}".to_sym). - and_return(:full_outage) - end + let("vendor_status_#{service}".to_sym) { :full_outage } it 'prevents an existing ial1 user from verifying their identity' do sign_in_with_idv_required(user: user, sms_or_totp: :sms) - expect(current_path).to eq vendor_outage_path expect(page).to have_content( - t('vendor_outage.blocked.idv.with_sp', service_provider: 'Test SP'), + strip_tags(t('idv.unavailable.idv_explanation.with_sp_html', sp: 'Test SP')), ) end @@ -228,15 +241,20 @@ def sign_in_with_idv_required(user:, sms_or_totp: :sms) click_on t('links.account.reactivate.without_key') click_on t('forms.buttons.continue') - expect(current_path).to eq vendor_outage_path - expect(page).to have_content(t('vendor_outage.blocked.idv.without_sp')) + expect(page).to have_content(t('idv.unavailable.idv_explanation.without_sp')) end it 'prevents a user from creating an account' do visit_idp_from_sp_with_ial2(:oidc) click_link t('links.create_account') - expect(current_path).to eq vendor_outage_path - expect(page).to have_content(t('vendor_outage.blocked.idv.generic')) + expect(page).to have_content( + strip_tags( + t( + 'idv.unavailable.idv_explanation.with_sp_html', + sp: 'Test SP', + ), + ), + ) end end end diff --git a/spec/lib/feature_management_spec.rb b/spec/lib/feature_management_spec.rb index 2af24ac3a32..5a06a83f728 100644 --- a/spec/lib/feature_management_spec.rb +++ b/spec/lib/feature_management_spec.rb @@ -461,4 +461,40 @@ end end end + + describe '#idv_available?' do + let(:idv_available) { true } + let(:vendor_status_acuant) { :operational } + let(:vendor_status_lexisnexis_instant_verify) { :operational } + let(:vendor_status_lexisnexis_trueid) { :operational } + + before do + allow(IdentityConfig.store).to receive(:idv_available).and_return(idv_available) + allow(IdentityConfig.store).to receive(:vendor_status_acuant).and_return(vendor_status_acuant) + allow(IdentityConfig.store).to receive(:vendor_status_lexisnexis_instant_verify). + and_return(vendor_status_lexisnexis_instant_verify) + allow(IdentityConfig.store).to receive(:vendor_status_lexisnexis_trueid). + and_return(vendor_status_lexisnexis_trueid) + end + + it 'returns true by default' do + expect(FeatureManagement.idv_available?).to eql(true) + end + + context 'idv has been disabled using config flag' do + let(:idv_available) { false } + it 'returns false' do + expect(FeatureManagement.idv_available?).to eql(false) + end + end + + %w[acuant lexisnexis_instant_verify lexisnexis_trueid].each do |service| + context "#{service} is in :full_outage" do + let("vendor_status_#{service}".to_sym) { :full_outage } + it 'returns false' do + expect(FeatureManagement.idv_available?).to eql(false) + end + end + end + end end diff --git a/spec/services/outage_status_spec.rb b/spec/services/outage_status_spec.rb index 8c1f234bda0..84bb778b259 100644 --- a/spec/services/outage_status_spec.rb +++ b/spec/services/outage_status_spec.rb @@ -1,11 +1,8 @@ require 'rails_helper' describe OutageStatus do - let(:from) { nil } - let(:from_idv) { nil } - let(:sp) { nil } subject(:vendor_status) do - OutageStatus.new(from: from, from_idv: from_idv, sp: sp) + OutageStatus.new end it 'raises an error if passed an unknown vendor' do @@ -52,38 +49,8 @@ expect(subject.any_idv_vendor_outage?).to be end - context 'user coming from create_account' do - let(:from) { SignUp::RegistrationsController::CREATE_ACCOUNT } - - it 'returns the correct message' do - expect(subject.outage_message).to eq I18n.t('vendor_outage.blocked.idv.generic') - end - end - - context 'user coming from idv flow' do - let(:from) { :welcome } - let(:from_idv) { true } - - context 'no service_provider in session' do - it 'returns the correct message' do - expect(subject.outage_message).to eq( - I18n.t('vendor_outage.blocked.idv.without_sp'), - ) - end - end - - context 'with service_provider in session' do - let(:sp) { create(:service_provider) } - - it 'returns the correct message tailored to the service provider' do - expect(subject.outage_message).to eq( - I18n.t( - 'vendor_outage.blocked.idv.with_sp', - service_provider: sp.friendly_name, - ), - ) - end - end + it 'returns the correct message' do + expect(subject.outage_message).to eq I18n.t('vendor_outage.blocked.idv.generic') end end @@ -171,14 +138,6 @@ it 'returns default phone outage message' do expect(outage_message).to eq(t('vendor_outage.blocked.phone.default')) end - - context 'from idv' do - let(:from_idv) { true } - - it 'returns idv phone outage message' do - expect(outage_message).to eq(t('vendor_outage.blocked.phone.idv')) - end - end end end @@ -187,7 +146,7 @@ analytics = FakeAnalytics.new expect(analytics).to receive(:track_event).with( 'Vendor Outage', - redirect_from: from, + redirect_from: nil, vendor_status: OutageStatus::ALL_VENDORS.index_with do |_vendor| satisfy { |status| IdentityConfig::VENDOR_STATUS_OPTIONS.include?(status) } end, diff --git a/spec/views/idv/unavailable/show.html.erb_spec.rb b/spec/views/idv/unavailable/show.html.erb_spec.rb new file mode 100644 index 00000000000..502a5e2f796 --- /dev/null +++ b/spec/views/idv/unavailable/show.html.erb_spec.rb @@ -0,0 +1,52 @@ +require 'rails_helper' + +describe 'idv/unavailable/show.html.erb' do + let(:sp_name) { nil } + subject(:rendered) { render } + + before do + allow(view).to receive(:decorated_session).and_return( + instance_double(ServiceProviderSessionDecorator, sp_name: sp_name), + ) + end + + it 'sets a title' do + expect(view).to receive(:title).with(t('idv.titles.unavailable')) + render + end + it 'has an h1' do + expect(rendered).to have_selector('h1', text: t('idv.titles.unavailable')) + end + it 'links to the status page in a new window' do + expect(rendered).to have_selector( + 'a[target=_blank]', + text: t('idv.unavailable.status_page_link'), + ) + end + + describe('exit button') do + it 'is rendered' do + expect(rendered).to have_selector( + 'a', + text: t('idv.unavailable.exit_button', app_name: APP_NAME), + ) + end + it 'links to the right place' do + expect(rendered).to have_link( + t('idv.unavailable.exit_button', app_name: APP_NAME), + href: return_to_sp_failure_to_proof_path(location: 'unavailable'), + ) + end + end + + it 'does not render any l13n markers' do + expect(rendered).not_to include('%{') + end + + context 'with sp' do + let(:sp_name) { 'Department of Ice Cream' } + it 'renders the explanation with the sp name' do + expect(rendered).to include(sp_name) + end + end +end From 69071c2a4e32789b16ead7e2a4675b0a61f31165 Mon Sep 17 00:00:00 2001 From: Tim Bradley <90272033+NavaTim@users.noreply.github.com> Date: Wed, 5 Apr 2023 14:43:57 -0700 Subject: [PATCH 17/25] LG-9272: Record in-person enrollment status check completion time (#8136) * LG-9272: Record in-person enrollment status check completion time changelog: Internal, In-Person Proofing, Record in-person enrollment status check completion time * LG-9272: Update enrollment status check completed field * LG-9272: Test status completed timestamp field setting --- app/jobs/get_usps_proofing_results_job.rb | 26 +++++++++-- app/models/in_person_enrollment.rb | 5 +++ ...k_completed_at_to_in_person_enrollments.rb | 6 +++ db/schema.rb | 3 +- .../get_usps_proofing_results_job_spec.rb | 44 ++++++++++++++++++- spec/models/in_person_enrollment_spec.rb | 18 ++++++++ 6 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 db/primary_migrate/20230403232935_add_status_check_completed_at_to_in_person_enrollments.rb diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb index f7f026edc7b..467f21a2ec2 100644 --- a/app/jobs/get_usps_proofing_results_job.rb +++ b/app/jobs/get_usps_proofing_results_job.rb @@ -117,6 +117,8 @@ def enrollment_analytics_attributes(enrollment, complete:) enrollment_code: enrollment.enrollment_code, enrollment_id: enrollment.id, minutes_since_last_status_check: enrollment.minutes_since_last_status_check, + minutes_since_last_status_check_completed: + enrollment.minutes_since_last_status_check_completed, minutes_since_last_status_update: enrollment.minutes_since_last_status_update, minutes_since_established: enrollment.minutes_since_established, minutes_to_completion: complete ? enrollment.minutes_since_established : nil, @@ -157,6 +159,7 @@ def handle_bad_request_error(err, enrollment) **enrollment_analytics_attributes(enrollment, complete: false), response_message: response_message, ) + enrollment.update(status_check_completed_at: Time.zone.now) elsif response_message&.match(IPP_EXPIRED_ERROR_MESSAGE) handle_expired_status_update(enrollment, err.response, response_message) elsif response_message == IPP_INVALID_ENROLLMENT_CODE_MESSAGE % enrollment.enrollment_code @@ -248,7 +251,11 @@ def handle_unsupported_id_type(enrollment, response) primary_id_type: response['primaryIdType'], reason: 'Unsupported ID type', ) - enrollment.update(status: :failed, proofed_at: proofed_at) + enrollment.update( + status: :failed, + proofed_at: proofed_at, + status_check_completed_at: Time.zone.now, + ) send_failed_email(enrollment.user, enrollment) analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( @@ -265,7 +272,10 @@ def handle_expired_status_update(enrollment, response, response_message) passed: false, reason: 'Enrollment has expired', ) - enrollment.update(status: :expired) + enrollment.update( + status: :expired, + status_check_completed_at: Time.zone.now, + ) begin send_deadline_passed_email(enrollment.user, enrollment) unless enrollment.deadline_passed_sent @@ -321,7 +331,11 @@ def handle_failed_status(enrollment, response) reason: 'Failed status', ) - enrollment.update(status: :failed, proofed_at: proofed_at) + enrollment.update( + status: :failed, + proofed_at: proofed_at, + status_check_completed_at: Time.zone.now, + ) if response['fraudSuspected'] send_failed_fraud_email(enrollment.user, enrollment) analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( @@ -347,7 +361,11 @@ def handle_successful_status_update(enrollment, response) reason: 'Successful status update', ) enrollment.profile.activate - enrollment.update(status: :passed, proofed_at: proofed_at) + enrollment.update( + status: :passed, + proofed_at: proofed_at, + status_check_completed_at: Time.zone.now, + ) analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( **email_analytics_attributes(enrollment), email_type: 'Success', diff --git a/app/models/in_person_enrollment.rb b/app/models/in_person_enrollment.rb index d3e450458fc..41a5a2a2e26 100644 --- a/app/models/in_person_enrollment.rb +++ b/app/models/in_person_enrollment.rb @@ -73,6 +73,11 @@ def minutes_since_last_status_check (Time.zone.now - status_check_attempted_at).seconds.in_minutes.round(2) end + def minutes_since_last_status_check_completed + return unless status_check_completed_at.present? + (Time.zone.now - status_check_completed_at).seconds.in_minutes.round(2) + end + def minutes_since_last_status_update return unless status_updated_at.present? (Time.zone.now - status_updated_at).seconds.in_minutes.round(2) diff --git a/db/primary_migrate/20230403232935_add_status_check_completed_at_to_in_person_enrollments.rb b/db/primary_migrate/20230403232935_add_status_check_completed_at_to_in_person_enrollments.rb new file mode 100644 index 00000000000..eec0281108e --- /dev/null +++ b/db/primary_migrate/20230403232935_add_status_check_completed_at_to_in_person_enrollments.rb @@ -0,0 +1,6 @@ +class AddStatusCheckCompletedAtToInPersonEnrollments < ActiveRecord::Migration[7.0] + def change + add_column :in_person_enrollments, :status_check_completed_at, :datetime, + comment: 'The last time a status check was successfully completed' + end +end diff --git a/db/schema.rb b/db/schema.rb index 5dce1006baa..1110da21d52 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_03_22_000756) do +ActiveRecord::Schema[7.0].define(version: 2023_04_03_232935) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "pgcrypto" @@ -310,6 +310,7 @@ t.boolean "deadline_passed_sent", default: false, comment: "deadline passed email sent for expired enrollment" t.datetime "proofed_at", precision: nil, comment: "timestamp when user attempted to proof at a Post Office" t.boolean "capture_secondary_id_enabled", default: false, comment: "record and proof state ID and residential addresses separately" + t.datetime "status_check_completed_at", comment: "The last time a status check was successfully completed" t.index ["profile_id"], name: "index_in_person_enrollments_on_profile_id" t.index ["status_check_attempted_at"], name: "index_in_person_enrollments_on_status_check_attempted_at", where: "(status = 1)" t.index ["unique_id"], name: "index_in_person_enrollments_on_unique_id", unique: true diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb index 5a8fc715819..05ee63aee19 100644 --- a/spec/jobs/get_usps_proofing_results_job_spec.rb +++ b/spec/jobs/get_usps_proofing_results_job_spec.rb @@ -6,6 +6,7 @@ pending_enrollment.update( enrollment_established_at: Time.zone.now - 3.days, status_check_attempted_at: Time.zone.now - 15.minutes, + status_check_completed_at: Time.zone.now - 17.minutes, status_updated_at: Time.zone.now - 2.days, ) @@ -21,6 +22,7 @@ fraud_suspected: response['fraudSuspected'], issuer: pending_enrollment.issuer, minutes_since_last_status_check: 15.0, + minutes_since_last_status_check_completed: 17.0, minutes_since_last_status_update: 2.days.in_minutes, minutes_to_completion: 3.days.in_minutes, minutes_since_established: 3.days.in_minutes, @@ -72,6 +74,7 @@ pending_enrollment.reload expect(pending_enrollment.status_updated_at).to eq(Time.zone.now) expect(pending_enrollment.status_check_attempted_at).to eq(Time.zone.now) + expect(pending_enrollment.status_check_completed_at).to eq(Time.zone.now) expect(pending_enrollment.status).to eq(status) expect(pending_enrollment.profile.active).to eq(passed) end @@ -116,6 +119,20 @@ expect(pending_enrollment.status_check_attempted_at).to eq(Time.zone.now) end end + + it 'does not update the status_check_completed_at timestamp' do + freeze_time do + pending_enrollment.update( + status_check_attempted_at: Time.zone.now - 1.day, + status_updated_at: Time.zone.now - 2.days, + ) + job.perform(Time.zone.now) + + pending_enrollment.reload + expect(pending_enrollment.status_updated_at).to eq(Time.zone.now - 2.days) + expect(pending_enrollment.status_check_completed_at).to be_nil + end + end end RSpec.shared_examples 'enrollment_encountering_an_error_that_has_a_nil_response' do |error_type:| @@ -132,6 +149,14 @@ ), ) end + + it 'does not update the status_check_completed_at timestamp' do + freeze_time do + job.perform(Time.zone.now) + pending_enrollment.reload + expect(pending_enrollment.status_check_completed_at).to be_nil + end + end end RSpec.describe GetUspsProofingResultsJob do @@ -227,7 +252,12 @@ expect(pending_enrollments.pluck(:status_check_attempted_at)).to( all(eq nil), - 'failed test precondition: pending enrollments must not have status check time set', + 'failed test precondition: pending enrollments must not set status check attempted time', + ) + + expect(pending_enrollments.pluck(:status_check_completed_at)).to( + all(eq nil), + 'failed test precondition: pending enrollments must not set status check completed time', ) freeze_time do @@ -239,7 +269,16 @@ pluck(:status_check_attempted_at), ).to( all(eq Time.zone.now), - 'job must update status check time for all pending enrollments', + 'job must update status check attempted time for all pending enrollments', + ) + + expect( + pending_enrollments. + map(&:reload). + pluck(:status_check_completed_at), + ).to( + all(eq Time.zone.now), + 'job must update status check completed time for all pending enrollments', ) end end @@ -885,6 +924,7 @@ expect(pending_enrollment.enrollment_established_at).to eq(Time.zone.now - 3.days) expect(pending_enrollment.status_updated_at).to eq(Time.zone.now - 1.day) expect(pending_enrollment.status_check_attempted_at).to eq(Time.zone.now) + expect(pending_enrollment.status_check_completed_at).to eq(Time.zone.now) end expect(pending_enrollment.profile.active).to eq(false) diff --git a/spec/models/in_person_enrollment_spec.rb b/spec/models/in_person_enrollment_spec.rb index d15317ad867..78983e05c15 100644 --- a/spec/models/in_person_enrollment_spec.rb +++ b/spec/models/in_person_enrollment_spec.rb @@ -219,6 +219,24 @@ end end + describe 'minutes_since_last_status_check_completed' do + let(:enrollment) do + create( + :in_person_enrollment, :passed, status_check_completed_at: Time.zone.now - 2.hours + ) + end + + it 'returns number of minutes since last status check was completed' do + expect(enrollment.minutes_since_last_status_check_completed).to be_within(0.01).of(120) + end + + it 'returns nil if enrollment has not completed a status check' do + enrollment.status_check_completed_at = nil + + expect(enrollment.minutes_since_last_status_check_completed).to eq(nil) + end + end + describe 'minutes_since_status_updated' do let(:enrollment) do enrollment = create(:in_person_enrollment, :passed) From ce7585e7e1f309b2ce870d6048c01fee20cc9c14 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Wed, 5 Apr 2023 15:55:54 -0700 Subject: [PATCH 18/25] Stop writing data to reports.log (LG-9415) (#8143) * Stop writing data to reports.log (LG-9415) changelog: Internal, Reporting, Remove unused data logging --- app/jobs/reports/base_report.rb | 8 ---- app/jobs/reports/sp_user_counts_report.rb | 45 +------------------ lib/identity_job_log_subscriber.rb | 4 -- .../reports/sp_user_counts_report_spec.rb | 43 ------------------ 4 files changed, 1 insertion(+), 99 deletions(-) diff --git a/app/jobs/reports/base_report.rb b/app/jobs/reports/base_report.rb index 33d2a5efe17..e424ffc8f49 100644 --- a/app/jobs/reports/base_report.rb +++ b/app/jobs/reports/base_report.rb @@ -50,14 +50,6 @@ def save_report(report_name, body, extension:) upload_file_to_s3_timestamped_and_latest(report_name, body, extension) end - def track_report_data_event(event, hash = {}) - write_hash_to_reports_log({ name: event, time: Time.zone.now.iso8601 }.merge(hash)) - end - - def write_hash_to_reports_log(log_hash) - reports_logger.info(log_hash.to_json) - end - def reports_logger @reports_logger ||= ActiveSupport::Logger.new(Rails.root.join('log', 'reports.log')) end diff --git a/app/jobs/reports/sp_user_counts_report.rb b/app/jobs/reports/sp_user_counts_report.rb index 64e8e76cc04..f40e9c3d319 100644 --- a/app/jobs/reports/sp_user_counts_report.rb +++ b/app/jobs/reports/sp_user_counts_report.rb @@ -9,50 +9,7 @@ def perform(_date) Db::Identity::SpUserCounts.call end - track_report_data_events(user_counts) - results = save_report(REPORT_NAME, user_counts.to_json, extension: 'json') - - track_global_user_total_events - results - end - - private - - def track_report_data_events(user_counts) - user_counts.each do |hash| - track_report_data_event( - 'Report SP User Counts', - issuer: hash['issuer'], - user_total: hash['total'], - ial1_user_total: hash['ial1_total'], - ial2_user_total: hash['ial2_total'], - app_id: hash['app_id'].to_s, - ) - end - end - - def track_global_user_total_events - track_global_registered_users - track_users_linked_to_sps(ial: 1, event: 'Report IAL1 Users Linked to SPs Count') - track_users_linked_to_sps(ial: 2, event: 'Report IAL2 Users Linked to SPs Count') - end - - def track_global_registered_users - transaction_with_timeout do - track_report_data_event( - 'Report Registered Users Count', - count: Funnel::Registration::TotalRegisteredCount.call, - ) - end - end - - def track_users_linked_to_sps(ial:, event:) - transaction_with_timeout do - track_report_data_event( - event, - count: ServiceProviderIdentity.where(ial: ial).select(:user_id).distinct.count, - ) - end + save_report(REPORT_NAME, user_counts.to_json, extension: 'json') end end end diff --git a/lib/identity_job_log_subscriber.rb b/lib/identity_job_log_subscriber.rb index 6948cc5feb4..2adc8c6d3ab 100644 --- a/lib/identity_job_log_subscriber.rb +++ b/lib/identity_job_log_subscriber.rb @@ -117,10 +117,6 @@ def logger end end - def self.reports_logger - @reports_logger ||= ActiveSupport::Logger.new(Rails.root.join('log', 'reports.log')) - end - def self.worker_logger @worker_logger ||= ActiveSupport::Logger.new(Rails.root.join('log', 'workers.log')) end diff --git a/spec/jobs/reports/sp_user_counts_report_spec.rb b/spec/jobs/reports/sp_user_counts_report_spec.rb index 70a2c1d9cea..32874b74eb5 100644 --- a/spec/jobs/reports/sp_user_counts_report_spec.rb +++ b/spec/jobs/reports/sp_user_counts_report_spec.rb @@ -10,49 +10,6 @@ expect(subject.perform(Time.zone.today)).to eq('[]') end - it 'logs to analytics' do - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id) - ServiceProviderIdentity.create(user_id: 1, service_provider: issuer, uuid: 'foo2', ial: 1) - ServiceProviderIdentity.create(user_id: 2, service_provider: issuer, uuid: 'foo3', ial: 1) - ServiceProviderIdentity.create(user_id: 3, service_provider: issuer, uuid: 'foo4', ial: 2) - freeze_time do - timestamp = Time.zone.now.iso8601 - expect(subject).to receive(:write_hash_to_reports_log).with( - { - app_id: app_id, - ial1_user_total: 3, - ial2_user_total: 0, - issuer: issuer, - name: 'Report SP User Counts', - time: timestamp, - user_total: 3, - }, - ) - expect(subject).to receive(:write_hash_to_reports_log).with( - { - name: 'Report Registered Users Count', - time: timestamp, - count: 0, - }, - ) - expect(subject).to receive(:write_hash_to_reports_log).with( - { - name: 'Report IAL1 Users Linked to SPs Count', - time: timestamp, - count: 2, - }, - ) - expect(subject).to receive(:write_hash_to_reports_log).with( - { - name: 'Report IAL2 Users Linked to SPs Count', - time: timestamp, - count: 1, - }, - ) - subject.perform(Time.zone.today) - end - end - it 'returns the total user counts per sp broken down by ial1 and ial2' do ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id) ServiceProviderIdentity.create(user_id: 1, service_provider: issuer, uuid: 'foo1') From 01d28db5a1ce44fc621177496b891b246c4233d6 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 6 Apr 2023 08:40:21 -0400 Subject: [PATCH 19/25] Remove unused StoreSpMetadataInSession#event_attributes (#8140) changelog: Internal, Code Quality, Remove unused code --- app/services/store_sp_metadata_in_session.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/services/store_sp_metadata_in_session.rb b/app/services/store_sp_metadata_in_session.rb index 9e573f286f3..507ac40724c 100644 --- a/app/services/store_sp_metadata_in_session.rb +++ b/app/services/store_sp_metadata_in_session.rb @@ -21,14 +21,6 @@ def ial_context @ial_context ||= IalContext.new(ial: sp_request.ial, service_provider: service_provider) end - def event_attributes - { - event: 'StoreSpMetadataInSession', - request_id_present: request_id.present?, - sp_request_class: sp_request.class.to_s, - }.to_json - end - def sp_request @sp_request ||= ServiceProviderRequestProxy.from_uuid(request_id) end From 3a095866041d563e8da1fc94c4060723f0940c16 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 6 Apr 2023 06:21:05 -0700 Subject: [PATCH 20/25] Update knapsack report (#8144) - Uses artifact from 69071c2a4e32789b16ead7e2a4675b0a61f31165 changelog: Internal, CI, Update knapsack report for spec timing --- knapsack_rspec_report.json | 1758 ++++++++++++++++++------------------ 1 file changed, 880 insertions(+), 878 deletions(-) diff --git a/knapsack_rspec_report.json b/knapsack_rspec_report.json index 1b4936dec3c..265cc93ec04 100644 --- a/knapsack_rspec_report.json +++ b/knapsack_rspec_report.json @@ -1,879 +1,881 @@ { - "spec/components/accordion_component_spec.rb": 0.049147011000000004, - "spec/components/alert_component_spec.rb": 0.108772105, - "spec/components/alert_icon_component_spec.rb": 0.039647655000000004, - "spec/components/barcode_component_spec.rb": 0.097015522, - "spec/components/base_component_spec.rb": 0.047929254, - "spec/components/block_link_component_spec.rb": 0.041102304, - "spec/components/button_component_spec.rb": 0.103327307, - "spec/components/click_observer_component_spec.rb": 0.017100947, - "spec/components/clipboard_button_component_spec.rb": 0.027529106999999997, - "spec/components/countdown_alert_component_spec.rb": 0.071084852, - "spec/components/countdown_component_spec.rb": 0.058935751, - "spec/components/download_button_component_spec.rb": 0.029449876, - "spec/components/flash_component_spec.rb": 0.035777779999999995, - "spec/components/form_link_component_spec.rb": 0.032759548, - "spec/components/icon_component_spec.rb": 0.046635709, - "spec/components/javascript_required_component_spec.rb": 0.083911971, - "spec/components/language_picker_component_spec.rb": 0.044003236, - "spec/components/memorable_date_component_spec.rb": 0.242044868, - "spec/components/modal_component_spec.rb": 0.063299125, - "spec/components/one_time_code_input_component_spec.rb": 0.14030613, - "spec/components/page_footer_component_spec.rb": 0.026011144, - "spec/components/page_heading_component_spec.rb": 0.035640573, - "spec/components/password_toggle_component_spec.rb": 0.097155429, - "spec/components/phone_input_component_spec.rb": 0.619180884, - "spec/components/print_button_component_spec.rb": 0.017636541, - "spec/components/process_list_component_spec.rb": 0.064359267, - "spec/components/spinner_button_component_spec.rb": 0.036441973, - "spec/components/status_page_component_spec.rb": 0.081372785, - "spec/components/step_indicator_component_spec.rb": 0.095723733, - "spec/components/step_indicator_step_component_spec.rb": 0.066855469, - "spec/components/submit_button_component_spec.rb": 0.036746543, - "spec/components/time_component_spec.rb": 0.03417052, - "spec/components/troubleshooting_options_component_spec.rb": 0.046549351, - "spec/components/validated_field_component_spec.rb": 0.109877303, - "spec/components/vendor_outage_alert_component_spec.rb": 0.057830144, - "spec/config/initializers/ab_tests_spec.rb": 0.008335407, - "spec/config/initializers/ahoy_spec.rb": 0.039570654, - "spec/config/initializers/async_exception_spec.rb": 0.020922435, - "spec/config/initializers/ext_digest_spec.rb": 0.006719152, - "spec/config/initializers/job_configurations_spec.rb": 0.079674567, - "spec/config/initializers/phonelib_spec.rb": 0.009395226, - "spec/config/initializers/secure_headers_spec.rb": 0.008657477, - "spec/controllers/account_reset/cancel_controller_spec.rb": 0.793307154, - "spec/controllers/account_reset/confirm_delete_account_controller_spec.rb": 0.028050355, - "spec/controllers/account_reset/confirm_request_controller_spec.rb": 0.079574126, - "spec/controllers/account_reset/delete_account_controller_spec.rb": 1.212332396, - "spec/controllers/account_reset/pending_controller_spec.rb": 0.327602232, - "spec/controllers/account_reset/recovery_options_controller_spec.rb": 0.155542707, - "spec/controllers/account_reset/request_controller_spec.rb": 1.569977809, - "spec/controllers/accounts/personal_keys_controller_spec.rb": 0.62853492, - "spec/controllers/accounts_controller_spec.rb": 0.272364962, - "spec/controllers/analytics_events_controller_spec.rb": 0.02353102, - "spec/controllers/api/irs_attempts_api_controller_spec.rb": 0.271741524, - "spec/controllers/api/verify/base_controller_spec.rb": 0.068762677, - "spec/controllers/api/verify/document_capture_controller_spec.rb": 0.237568261, - "spec/controllers/api/verify/document_capture_errors_controller_spec.rb": 0.210054909, - "spec/controllers/application_controller_spec.rb": 2.117976024, - "spec/controllers/concerns/effective_user_spec.rb": 0.073727609, - "spec/controllers/concerns/idv/document_capture_concern_spec.rb": 0.099133214, - "spec/controllers/concerns/idv/phone_otp_rate_limitable_spec.rb": 0.022041273, - "spec/controllers/concerns/idv/step_indicator_concern_spec.rb": 0.28566715, - "spec/controllers/concerns/idv/threat_metrix_concern_spec.rb": 0.21251878200000002, - "spec/controllers/concerns/idv_step_concern_spec.rb": 0.13809474800000002, - "spec/controllers/concerns/inherited_proofing_concern_spec.rb": 0.088302702, - "spec/controllers/concerns/render_condition_concern_spec.rb": 0.37033526, - "spec/controllers/concerns/verify_sp_attributes_concern_spec.rb": 0.398529965, - "spec/controllers/country_support_controller_spec.rb": 0.053931489, - "spec/controllers/event_disavowal_controller_spec.rb": 0.351596013, - "spec/controllers/fake_s3_controller_spec.rb": 2.511295649, - "spec/controllers/forgot_password_controller_spec.rb": 0.027224797000000002, - "spec/controllers/frontend_log_controller_spec.rb": 0.342427905, - "spec/controllers/health/database_controller_spec.rb": 0.048420299, - "spec/controllers/health/health_controller_spec.rb": 0.045889608, - "spec/controllers/health/outbound_controller_spec.rb": 0.070818091, - "spec/controllers/idv/cancellations_controller_spec.rb": 0.652963132, - "spec/controllers/idv/capture_doc_controller_spec.rb": 0.327192588, - "spec/controllers/idv/capture_doc_status_controller_spec.rb": 0.555131835, - "spec/controllers/idv/come_back_later_controller_spec.rb": 0.084183646, - "spec/controllers/idv/doc_auth_controller_spec.rb": 0.831820165, - "spec/controllers/idv/forgot_password_controller_spec.rb": 0.45275612600000004, - "spec/controllers/idv/gpo_controller_spec.rb": 0.890774696, - "spec/controllers/idv/gpo_verify_controller_spec.rb": 1.789293908, - "spec/controllers/idv/image_uploads_controller_spec.rb": 1.36737438, - "spec/controllers/idv/in_person/address_search_controller_spec.rb": 0.108375784, - "spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb": 0.216674961, - "spec/controllers/idv/in_person/usps_locations_controller_spec.rb": 2.831394503, - "spec/controllers/idv/in_person_controller_spec.rb": 0.209785071, - "spec/controllers/idv/inherited_proofing_cancellations_controller_spec.rb": 0.412491946, - "spec/controllers/idv/inherited_proofing_controller_spec.rb": 0.230617606, - "spec/controllers/idv/otp_delivery_method_controller_spec.rb": 0.46765666699999997, - "spec/controllers/idv/otp_verification_controller_spec.rb": 0.23485983, - "spec/controllers/idv/personal_key_controller_spec.rb": 1.698414453, - "spec/controllers/idv/phone_controller_spec.rb": 1.475145405, - "spec/controllers/idv/phone_errors_controller_spec.rb": 0.532586852, - "spec/controllers/idv/resend_otp_controller_spec.rb": 0.159267863, - "spec/controllers/idv/review_controller_spec.rb": 5.8206442890000005, - "spec/controllers/idv/session_errors_controller_spec.rb": 3.4046109600000003, - "spec/controllers/idv/sessions_controller_spec.rb": 0.328329708, - "spec/controllers/idv/setup_errors_controller_spec.rb": 0.043395489, - "spec/controllers/idv/verify_info_controller_spec.rb": 0.124265517, - "spec/controllers/idv_controller_spec.rb": 0.340371501, - "spec/controllers/mfa_confirmation_controller_spec.rb": 0.28578834399999997, - "spec/controllers/no_js_controller_spec.rb": 0.027093832999999998, - "spec/controllers/openid_connect/authorization_controller_spec.rb": 1.166386948, - "spec/controllers/openid_connect/certs_controller_spec.rb": 0.024938413, - "spec/controllers/openid_connect/configuration_controller_spec.rb": 0.029358142, - "spec/controllers/openid_connect/logout_controller_spec.rb": 1.617582479, - "spec/controllers/openid_connect/token_controller_spec.rb": 0.254868956, - "spec/controllers/openid_connect/user_info_controller_spec.rb": 0.198672963, - "spec/controllers/pages_controller_spec.rb": 0.058824031, - "spec/controllers/password_capture_controller_spec.rb": 0.124963287, - "spec/controllers/reactivate_account_controller_spec.rb": 0.223552417, - "spec/controllers/reauthn_required_controller_spec.rb": 0.11579423999999999, - "spec/controllers/redirect/contact_controller_spec.rb": 0.017144125, - "spec/controllers/redirect/help_center_controller_spec.rb": 0.059476430999999996, - "spec/controllers/redirect/return_to_sp_controller_spec.rb": 0.097941473, - "spec/controllers/risc/configuration_controller_spec.rb": 0.014132906, - "spec/controllers/risc/security_events_controller_spec.rb": 0.990391504, - "spec/controllers/saml_completion_controller_spec.rb": 0.039140058, - "spec/controllers/saml_idp_controller_spec.rb": 11.093810166, - "spec/controllers/saml_post_controller_spec.rb": 0.033944142999999996, - "spec/controllers/saml_signed_message_spec.rb": 0.439664603, - "spec/controllers/service_provider_controller_spec.rb": 0.087910502, - "spec/controllers/sign_out_controller_spec.rb": 0.065300148, - "spec/controllers/sign_up/cancellations_controller_spec.rb": 0.40229099, - "spec/controllers/sign_up/completions_controller_spec.rb": 0.547326205, - "spec/controllers/sign_up/email_confirmations_controller_spec.rb": 0.224425584, - "spec/controllers/sign_up/emails_controller_spec.rb": 0.033428642, - "spec/controllers/sign_up/passwords_controller_spec.rb": 0.178644169, - "spec/controllers/sign_up/registrations_controller_spec.rb": 1.39882121, - "spec/controllers/test/device_profiling_controller_spec.rb": 0.025794102, - "spec/controllers/test/piv_cac_authentication_test_subject_controller_spec.rb": 0.064482308, - "spec/controllers/test/push_notification_controller_spec.rb": 0.037449352, - "spec/controllers/test/telephony_controller_spec.rb": 0.042967176999999995, - "spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb": 0.715876689, - "spec/controllers/two_factor_authentication/options_controller_spec.rb": 0.410579153, - "spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb": 3.136869566, - "spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb": 0.99690613, - "spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb": 0.915621478, - "spec/controllers/two_factor_authentication/sms_opt_in_controller_spec.rb": 0.635024222, - "spec/controllers/two_factor_authentication/totp_verification_controller_spec.rb": 0.733835657, - "spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb": 0.355507404, - "spec/controllers/users/additional_mfa_required_controller_spec.rb": 0.18356746899999998, - "spec/controllers/users/authorization_confirmation_controller_spec.rb": 0.147308682, - "spec/controllers/users/backup_code_setup_controller_spec.rb": 1.6961684620000002, - "spec/controllers/users/delete_controller_spec.rb": 0.475854728, - "spec/controllers/users/edit_phone_controller_spec.rb": 0.147183221, - "spec/controllers/users/email_confirmations_controller_spec.rb": 1.448550375, - "spec/controllers/users/email_language_controller_spec.rb": 0.239465163, - "spec/controllers/users/emails_controller_spec.rb": 0.542180274, - "spec/controllers/users/forget_all_browsers_controller_spec.rb": 0.239115638, - "spec/controllers/users/mfa_selection_controller_spec.rb": 0.380249971, - "spec/controllers/users/passwords_controller_spec.rb": 1.479995975, - "spec/controllers/users/personal_keys_controller_spec.rb": 0.331246202, - "spec/controllers/users/phone_setup_controller_spec.rb": 0.266579666, - "spec/controllers/users/phones_controller_spec.rb": 0.20955723899999998, - "spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb": 0.999457506, - "spec/controllers/users/reset_passwords_controller_spec.rb": 2.175335544, - "spec/controllers/users/rules_of_use_controller_spec.rb": 0.468152071, - "spec/controllers/users/service_provider_revoke_controller_spec.rb": 0.432359624, - "spec/controllers/users/sessions_controller_spec.rb": 1.790181608, - "spec/controllers/users/totp_setup_controller_spec.rb": 4.111021269, - "spec/controllers/users/two_factor_authentication_controller_spec.rb": 2.157944642, - "spec/controllers/users/two_factor_authentication_setup_controller_spec.rb": 0.428352789, - "spec/controllers/users/verify_password_controller_spec.rb": 0.367195705, - "spec/controllers/users/verify_personal_key_controller_spec.rb": 0.875904318, - "spec/controllers/users/webauthn_setup_controller_spec.rb": 0.70466533, - "spec/controllers/vendor_outage_controller_spec.rb": 0.043053054, - "spec/decorators/device_decorator_spec.rb": 0.044806382, - "spec/decorators/email_context_spec.rb": 0.065239854, - "spec/decorators/event_decorator_spec.rb": 0.098268357, - "spec/decorators/mfa_context_spec.rb": 1.371234219, - "spec/decorators/service_provider_session_decorator_spec.rb": 0.321755287, - "spec/decorators/session_decorator_spec.rb": 0.062076096, - "spec/decorators/user_decorator_spec.rb": 1.016717048, - "spec/features/accessibility/idv_pages_spec.rb": 102.978934337, - "spec/features/accessibility/static_pages_spec.rb": 56.09555461, - "spec/features/accessibility/user_pages_spec.rb": 138.871200876, - "spec/features/accessibility/visitor_pages_spec.rb": 23.394869103, - "spec/features/account/backup_codes_spec.rb": 23.388061234, - "spec/features/account/device_spec.rb": 4.238363682, - "spec/features/account/unphishable_badge_spec.rb": 8.318415434999999, - "spec/features/account_connected_apps_spec.rb": 9.033295784, - "spec/features/account_creation/multiple_browsers_spec.rb": 20.211398447, - "spec/features/account_creation/sp_return_log_spec.rb": 5.493974012, - "spec/features/account_email_language_spec.rb": 13.354988754, - "spec/features/account_history_spec.rb": 4.342116669, - "spec/features/account_reset/cancel_request_spec.rb": 5.843557439, - "spec/features/account_reset/delete_account_spec.rb": 16.449192662, - "spec/features/account_reset/pending_request_spec.rb": 6.071581068, - "spec/features/device_tracking_spec.rb": 8.518126325, - "spec/features/event_disavowal_spec.rb": 53.574837167, - "spec/features/ialmax/saml_sign_in_spec.rb": 35.21371572, - "spec/features/idv/account_creation_spec.rb": 56.144554471, - "spec/features/idv/actions/cancel_link_sent_action_spec.rb": 4.882536451, - "spec/features/idv/actions/cancel_send_link_action_spec.rb": 4.757549789, - "spec/features/idv/actions/redo_document_capture_action_spec.rb": 23.409524570000002, - "spec/features/idv/analytics_spec.rb": 27.271190020000002, - "spec/features/idv/cancel_spec.rb": 30.504384713999997, - "spec/features/idv/clearing_and_restarting_spec.rb": 83.24677081600001, - "spec/features/idv/doc_auth/address_step_spec.rb": 27.730397611, - "spec/features/idv/doc_auth/agreement_step_spec.rb": 33.608453043, - "spec/features/idv/doc_auth/document_capture_step_spec.rb": 44.028447855, - "spec/features/idv/doc_auth/email_sent_step_spec.rb": 4.833393796, - "spec/features/idv/doc_auth/link_sent_step_spec.rb": 47.621717086, - "spec/features/idv/doc_auth/send_link_step_spec.rb": 40.572430571, - "spec/features/idv/doc_auth/ssn_step_spec.rb": 18.395156692, - "spec/features/idv/doc_auth/test_credentials_spec.rb": 15.221689832, - "spec/features/idv/doc_auth/upload_step_spec.rb": 18.631862249, - "spec/features/idv/doc_auth/verify_info_step_spec.rb": 26.987061057000002, - "spec/features/idv/doc_auth/verify_step_spec.rb": 116.05652094999999, - "spec/features/idv/doc_auth/welcome_step_spec.rb": 22.040493143, - "spec/features/idv/doc_capture/capture_complete_step_spec.rb": 6.307759956, - "spec/features/idv/doc_capture/document_capture_step_spec.rb": 86.823149064, - "spec/features/idv/gpo_disabled_spec.rb": 9.182235644, - "spec/features/idv/hybrid_flow_spec.rb": 28.979461954, - "spec/features/idv/in_person_spec.rb": 112.48211235400001, - "spec/features/idv/inherited_proofing/agreement_step_spec.rb": 27.898213038, - "spec/features/idv/inherited_proofing/analytics_spec.rb": 5.406147415, - "spec/features/idv/inherited_proofing/get_started_step_spec.rb": 8.919112244, - "spec/features/idv/inherited_proofing/inherited_proofing_cancel_spec.rb": 97.869064893, - "spec/features/idv/inherited_proofing/verify_info_step_spec.rb": 27.370937477, - "spec/features/idv/inherited_proofing/verify_wait_step_spec.rb": 32.942364572, - "spec/features/idv/phone_input_spec.rb": 14.514575307, - "spec/features/idv/phone_otp_rate_limiting_spec.rb": 29.446112104999997, - "spec/features/idv/proofing_components_spec.rb": 42.512160836999996, - "spec/features/idv/sp_handoff_spec.rb": 137.671309011, - "spec/features/idv/sp_requested_attributes_spec.rb": 74.8063194, - "spec/features/idv/steps/confirmation_step_spec.rb": 42.633386223, - "spec/features/idv/steps/forgot_password_step_spec.rb": 24.742056025000004, - "spec/features/idv/steps/gpo_otp_verification_step_spec.rb": 106.017550162, - "spec/features/idv/steps/gpo_step_spec.rb": 57.125811075, - "spec/features/idv/steps/in_person/verify_step_spec.rb": 11.197358651, - "spec/features/idv/steps/phone_otp_verification_step_spec.rb": 33.458809718000005, - "spec/features/idv/steps/phone_step_spec.rb": 196.557153647, - "spec/features/idv/steps/review_step_spec.rb": 46.152502773, - "spec/features/idv/threatmetrix_pending_spec.rb": 16.949332325, - "spec/features/idv/uak_password_spec.rb": 9.639697261, - "spec/features/idv/vendor_outage_spec.rb": 47.880928744, - "spec/features/irs_attempts_api/event_tracking_spec.rb": 23.299167091, - "spec/features/legacy_passwords_spec.rb": 18.011714479, - "spec/features/load_testing/email_sign_up_spec.rb": 4.478636518, - "spec/features/multi_factor_authentication/mfa_cta_spec.rb": 15.661944315, - "spec/features/multiple_emails/add_email_spec.rb": 74.432278243, - "spec/features/multiple_emails/email_management_spec.rb": 32.047586089999996, - "spec/features/multiple_emails/reset_password_spec.rb": 9.537514888, - "spec/features/multiple_emails/sign_in_spec.rb": 14.959968993, - "spec/features/multiple_emails/sp_sign_in_spec.rb": 21.934338037, - "spec/features/new_device_tracking_spec.rb": 13.500902486000001, - "spec/features/openid_connect/authorization_confirmation_spec.rb": 24.572898996, - "spec/features/openid_connect/openid_connect_spec.rb": 205.46179577, - "spec/features/openid_connect/phishing_resistant_required_spec.rb": 44.989927725, - "spec/features/openid_connect/redirect_uri_validation_spec.rb": 43.46192137, - "spec/features/phone/add_phone_spec.rb": 40.597530241, - "spec/features/phone/confirmation_spec.rb": 146.396855398, - "spec/features/phone/default_phone_selection_spec.rb": 23.700579428, - "spec/features/phone/edit_phone_spec.rb": 12.660619418, - "spec/features/phone/rate_limitting_spec.rb": 70.687632474, - "spec/features/phone/remove_phone_spec.rb": 8.708044022, - "spec/features/remember_device/cookie_expiration_spec.rb": 9.56998625, - "spec/features/remember_device/phone_spec.rb": 51.059496764, - "spec/features/remember_device/revocation_spec.rb": 56.235288777, - "spec/features/remember_device/session_expiration_spec.rb": 5.149553426, - "spec/features/remember_device/sp_expiration_spec.rb": 268.472075532, - "spec/features/remember_device/totp_spec.rb": 65.987402859, - "spec/features/remember_device/user_opted_preference_spec.rb": 35.608870363, - "spec/features/remember_device/webauthn_spec.rb": 124.748069825, - "spec/features/reports/authorization_count_spec.rb": 91.459592413, - "spec/features/reports/monthly_gpo_letter_requests_report_spec.rb": 16.182485294, - "spec/features/reports/sp_active_users_report_spec.rb": 9.465962556000001, - "spec/features/saml/authorization_confirmation_spec.rb": 31.964992879, - "spec/features/saml/ial1/account_creation_spec.rb": 13.865674372, - "spec/features/saml/ial1_sso_spec.rb": 67.435076633, - "spec/features/saml/ial2_sso_spec.rb": 38.057789857, - "spec/features/saml/multiple_endpoints_spec.rb": 33.496769253000004, - "spec/features/saml/phishing_resistant_required_spec.rb": 30.763752886, - "spec/features/saml/redirect_uri_validation_spec.rb": 4.95748826, - "spec/features/saml/saml_logout_spec.rb": 30.792872715999998, - "spec/features/saml/saml_relay_state_spec.rb": 19.590728821, - "spec/features/saml/saml_spec.rb": 119.776592439, - "spec/features/services/idv/inherited_proofing/va/mocks/service_spec.rb": 4.038326983, - "spec/features/session/decryption_spec.rb": 4.256088043, - "spec/features/session/timeout_spec.rb": 12.524386905, - "spec/features/sign_in/banned_users_spec.rb": 15.752049699, - "spec/features/sign_in/remember_device_default_spec.rb": 13.080639506, - "spec/features/sign_in/sp_return_log_spec.rb": 4.824498238, - "spec/features/sign_in/two_factor_options_spec.rb": 62.129132518, - "spec/features/sp_cost_tracking_spec.rb": 55.082879594000005, - "spec/features/two_factor_authentication/backup_code_sign_up_spec.rb": 30.868415282, - "spec/features/two_factor_authentication/change_factor_spec.rb": 20.45261191, - "spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb": 34.73611352, - "spec/features/two_factor_authentication/multiple_tabs_spec.rb": 13.295791412, - "spec/features/two_factor_authentication/sign_in_spec.rb": 149.34513190799998, - "spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb": 9.318756492999999, - "spec/features/users/non_restricted_required_sign_in_spec.rb": 22.969656438, - "spec/features/users/password_recovery_via_recovery_code_spec.rb": 57.222423066000005, - "spec/features/users/password_reset_with_pending_profile_spec.rb": 9.117393059000001, - "spec/features/users/piv_cac_management_spec.rb": 40.478984125, - "spec/features/users/regenerate_personal_key_spec.rb": 11.362544527, - "spec/features/users/sign_in_irs_spec.rb": 42.079423733, - "spec/features/users/sign_in_spec.rb": 540.879714153, - "spec/features/users/sign_out_spec.rb": 4.259124252, - "spec/features/users/sign_up_spec.rb": 164.288494001, - "spec/features/users/totp_management_spec.rb": 24.442206140000003, - "spec/features/users/user_edit_spec.rb": 4.468454409, - "spec/features/users/user_profile_spec.rb": 49.651148085, - "spec/features/users/verify_profile_spec.rb": 18.665291157, - "spec/features/visitors/bad_password_spec.rb": 4.814326332, - "spec/features/visitors/email_confirmation_spec.rb": 29.018051282000002, - "spec/features/visitors/i18n_spec.rb": 51.849335752, - "spec/features/visitors/js_disabled_spec.rb": 8.39112344, - "spec/features/visitors/navigation_spec.rb": 4.094604047, - "spec/features/visitors/password_recovery_spec.rb": 86.025270207, - "spec/features/visitors/resend_email_confirmation_spec.rb": 22.605908782, - "spec/features/visitors/set_password_spec.rb": 34.726599573, - "spec/features/visitors/sign_up_with_email_spec.rb": 37.329449042, - "spec/features/webauthn/hidden_spec.rb": 28.581912582, - "spec/features/webauthn/management_spec.rb": 70.053166159, - "spec/features/webauthn/sign_in_spec.rb": 14.182489745000002, - "spec/features/webauthn/sign_up_spec.rb": 19.039691697000002, - "spec/forms/add_user_email_form_spec.rb": 0.479784722, - "spec/forms/api/verify/document_capture_errors_delete_form_spec.rb": 0.029721246, - "spec/forms/delete_user_email_form_spec.rb": 0.310914606, - "spec/forms/edit_phone_form_spec.rb": 0.258150779, - "spec/forms/event_disavowal/password_reset_from_disavowal_form_spec.rb": 3.682894634, - "spec/forms/gpo_verify_form_spec.rb": 1.142967115, - "spec/forms/idv/api_document_verification_form_spec.rb": 0.45293001800000005, - "spec/forms/idv/api_document_verification_status_form_spec.rb": 0.140759624, - "spec/forms/idv/api_image_upload_form_spec.rb": 1.253924595, - "spec/forms/idv/doc_pii_form_spec.rb": 0.064349871, - "spec/forms/idv/inherited_proofing/base_form_spec.rb": 0.086049866, - "spec/forms/idv/inherited_proofing/va/form_spec.rb": 0.197490471, - "spec/forms/idv/phone_confirmation_otp_verification_form_spec.rb": 0.208945709, - "spec/forms/idv/phone_form_spec.rb": 1.081262927, - "spec/forms/idv/ssn_form_spec.rb": 0.171843121, - "spec/forms/idv/ssn_format_form_spec.rb": 0.08519818900000001, - "spec/forms/new_phone_form_spec.rb": 1.161641731, - "spec/forms/openid_connect_authorize_form_spec.rb": 0.518811745, - "spec/forms/openid_connect_logout_form_spec.rb": 0.706959874, - "spec/forms/openid_connect_token_form_spec.rb": 1.9757069170000001, - "spec/forms/otp_delivery_selection_form_spec.rb": 0.119669807, - "spec/forms/otp_verification_form_spec.rb": 0.218209233, - "spec/forms/password_form_spec.rb": 0.97725235, - "spec/forms/password_reset_email_form_spec.rb": 0.080207263, - "spec/forms/personal_key_form_spec.rb": 0.065018962, - "spec/forms/register_user_email_form_spec.rb": 2.78392862, - "spec/forms/reset_password_form_spec.rb": 1.1610412989999999, - "spec/forms/security_event_form_spec.rb": 2.911133848, - "spec/forms/totp_setup_form_spec.rb": 0.18822861400000002, - "spec/forms/totp_verification_form_spec.rb": 0.069494468, - "spec/forms/two_factor_authentication/phone_deletion_form_spec.rb": 0.535660895, - "spec/forms/two_factor_login_options_form_spec.rb": 0.037470828, - "spec/forms/two_factor_options_form_spec.rb": 0.325853875, - "spec/forms/update_email_language_form_spec.rb": 0.08954489, - "spec/forms/update_user_password_form_spec.rb": 1.092777003, - "spec/forms/user_piv_cac_login_form_spec.rb": 0.067772621, - "spec/forms/user_piv_cac_setup_form_spec.rb": 0.201820367, - "spec/forms/user_piv_cac_verification_form_spec.rb": 0.144574521, - "spec/forms/verify_password_form_spec.rb": 0.134231806, - "spec/forms/verify_personal_key_form_spec.rb": 0.284375214, - "spec/forms/webauthn_setup_form_spec.rb": 0.338584243, - "spec/forms/webauthn_verification_form_spec.rb": 0.100489014, - "spec/forms/webauthn_visit_form_spec.rb": 0.175756424, - "spec/helpers/application_helper_spec.rb": 0.026707177, - "spec/helpers/asset_helper_spec.rb": 0.017005984000000002, - "spec/helpers/aws_s3_helper_spec.rb": 0.073577482, - "spec/helpers/go_back_helper_spec.rb": 0.060757685, - "spec/helpers/link_helper_spec.rb": 0.101946363, - "spec/helpers/locale_helper_spec.rb": 0.088891574, - "spec/helpers/script_helper_spec.rb": 0.099531843, - "spec/helpers/session_timeout_warning_helper_spec.rb": 0.069595488, - "spec/i18n_spec.rb": 37.720227783, - "spec/jobs/address_proofing_job_spec.rb": 0.17462343, - "spec/jobs/application_job_spec.rb": 0.009142006, - "spec/jobs/document_proofing_job_spec.rb": 0.50437029, - "spec/jobs/get_usps_proofing_results_job_spec.rb": 6.619208557, - "spec/jobs/gpo_daily_job_spec.rb": 0.099331143, - "spec/jobs/heartbeat_job_spec.rb": 0.052770776, - "spec/jobs/in_person/email_reminder_job_spec.rb": 0.887409784, - "spec/jobs/inherited_proofing_job_spec.rb": 0.015349274, - "spec/jobs/irs_attempts_events_batch_job_spec.rb": 0.330945908, - "spec/jobs/job_helpers/encryption_helper_spec.rb": 0.008010765, - "spec/jobs/job_helpers/s3_helper_spec.rb": 0.110703917, - "spec/jobs/job_helpers/stale_job_helper_spec.rb": 0.030398194, - "spec/jobs/job_helpers/timer_spec.rb": 0.022952948, - "spec/jobs/phone_number_opt_out_sync_job_spec.rb": 0.066530903, - "spec/jobs/psql_stats_job_spec.rb": 0.039264083, - "spec/jobs/reports/agreement_summary_report_spec.rb": 0.108298018, - "spec/jobs/reports/base_report_spec.rb": 0.008733073, - "spec/jobs/reports/combined_invoice_supplement_report_spec.rb": 0.36172781, - "spec/jobs/reports/daily_auths_report_spec.rb": 0.093644721, - "spec/jobs/reports/daily_dropoffs_report_spec.rb": 0.13644600899999998, - "spec/jobs/reports/deleted_user_accounts_report_spec.rb": 0.105382982, - "spec/jobs/reports/irs_weekly_summary_report_spec.rb": 0.296948832, - "spec/jobs/reports/month_helper_spec.rb": 0.019340088999999998, - "spec/jobs/reports/query_helpers_spec.rb": 0.020601285, - "spec/jobs/reports/sp_active_users_report_spec.rb": 0.08714355900000001, - "spec/jobs/reports/sp_user_counts_report_spec.rb": 0.05578129, - "spec/jobs/reports/total_ial2_costs_report_spec.rb": 0.034388406, - "spec/jobs/reports/total_monthly_auths_report_spec.rb": 0.045500935, - "spec/jobs/reports/verification_failures_report_spec.rb": 0.455579546, - "spec/jobs/resolution_proofing_job_spec.rb": 0.758022259, - "spec/jobs/risc_delivery_job_spec.rb": 0.209109026, - "spec/jobs/threat_metrix_js_verification_job_spec.rb": 1.030960332, - "spec/lib/ab_test_bucket_spec.rb": 0.06390188399999999, - "spec/lib/analytics_events_documenter_spec.rb": 0.248858927, - "spec/lib/app_artifacts_spec.rb": 0.048771506, - "spec/lib/asset_sources_spec.rb": 0.1362476, - "spec/lib/aws/ses_spec.rb": 0.038266803, - "spec/lib/base16_spec.rb": 0.032634536, - "spec/lib/data_requests/create_email_addresses_report_spec.rb": 0.026106901, - "spec/lib/data_requests/create_mfa_configurations_report_spec.rb": 0.15112360600000002, - "spec/lib/data_requests/create_user_events_report_spec.rb": 0.050788221, - "spec/lib/data_requests/create_user_report_spec.rb": 0.10290339700000001, - "spec/lib/data_requests/fetch_cloudwatch_logs_spec.rb": 0.024198622, - "spec/lib/data_requests/lookup_shared_device_users_spec.rb": 0.068085213, - "spec/lib/data_requests/lookup_user_by_uuid_spec.rb": 0.063915739, - "spec/lib/data_requests/write_cloudwatch_logs_spec.rb": 0.027318553, - "spec/lib/data_requests/write_user_events_spec.rb": 0.011272968, - "spec/lib/data_requests/write_user_info_spec.rb": 0.01058327, - "spec/lib/deploy/activate_spec.rb": 0.090855127, - "spec/lib/feature_management_spec.rb": 0.273514838, - "spec/lib/fingerprinter_spec.rb": 0.018583016, - "spec/lib/headers_filter_spec.rb": 0.006812243, - "spec/lib/identity_config_spec.rb": 0.026371801, - "spec/lib/identity_job_log_subscriber_spec.rb": 0.206703883, - "spec/lib/linters/errors_add_linter_spec.rb": 0.047610634, - "spec/lib/linters/image_size_linter_spec.rb": 0.094331455, - "spec/lib/linters/localized_validation_message_linter_spec.rb": 0.08268767, - "spec/lib/linters/mail_later_linter_spec.rb": 0.091152605, - "spec/lib/linters/redirect_back_linter_spec.rb": 0.077695647, - "spec/lib/linters/url_options_linter_spec.rb": 0.12496467800000001, - "spec/lib/makefile_help_parser_spec.rb": 0.089192572, - "spec/lib/otp_code_generator_spec.rb": 0.040730353, - "spec/lib/pinpoint_supported_countries_spec.rb": 0.127769791, - "spec/lib/query_tracker_spec.rb": 0.044862226, - "spec/lib/session_encryptor_spec.rb": 0.098413281, - "spec/lib/tasks/dev_rake_spec.rb": 12.162226285000001, - "spec/lib/tasks/partners_rake_spec.rb": 0.886899062, - "spec/lib/tasks/review_profile_spec.rb": 0.411175656, - "spec/lib/tasks/rotate_rake_spec.rb": 0.10711069200000001, - "spec/lib/telephony/alert_sender_spec.rb": 0.07433495600000001, - "spec/lib/telephony/otp_sender_spec.rb": 0.22194904000000001, - "spec/lib/telephony/pinpoint/aws_credential_builder_spec.rb": 0.029442157, - "spec/lib/telephony/pinpoint/opt_out_manager_spec.rb": 0.082387123, - "spec/lib/telephony/pinpoint/sms_sender_spec.rb": 0.249128754, - "spec/lib/telephony/pinpoint/voice_sender_spec.rb": 0.109370858, - "spec/lib/telephony/response_spec.rb": 0.047005193, - "spec/lib/telephony/telephony_spec.rb": 0.084029964, - "spec/lib/telephony/test/call_spec.rb": 0.048464306, - "spec/lib/telephony/test/message_spec.rb": 0.044993153, - "spec/lib/telephony/test/sms_sender_spec.rb": 0.05212054, - "spec/lib/telephony/test/voice_sender_spec.rb": 0.01956261, - "spec/lib/telephony/util_spec.rb": 0.00737186, - "spec/lib/utf8_sanitizer_spec.rb": 0.061775448, - "spec/mailers/previews/user_mailer_preview_spec.rb": 0.279313044, - "spec/mailers/report_mailer_spec.rb": 0.09894568299999999, - "spec/mailers/user_mailer_spec.rb": 3.351854277, - "spec/models/account_reset_request_spec.rb": 0.063627305, - "spec/models/agency_identity_spec.rb": 0.034431008, - "spec/models/agency_spec.rb": 0.046146805, - "spec/models/agreements/iaa_gtc_spec.rb": 0.296810918, - "spec/models/agreements/iaa_order_spec.rb": 0.504770998, - "spec/models/agreements/iaa_spec.rb": 0.312157665, - "spec/models/agreements/integration_spec.rb": 0.369569055, - "spec/models/agreements/integration_status_spec.rb": 0.079110488, - "spec/models/agreements/integration_usage_spec.rb": 0.267389804, - "spec/models/agreements/partner_account_spec.rb": 0.211296024, - "spec/models/agreements/partner_account_status_spec.rb": 0.089256006, - "spec/models/anonymous_user_spec.rb": 0.038836213, - "spec/models/backup_code_configuration_spec.rb": 1.3155075330000001, - "spec/models/concerns/user_otp_methods_spec.rb": 0.010580832, - "spec/models/deleted_user_spec.rb": 0.0996753, - "spec/models/device_spec.rb": 0.09487378, - "spec/models/document_capture_session_spec.rb": 0.12486395, - "spec/models/email_address_spec.rb": 0.254197475, - "spec/models/event_spec.rb": 0.049106344999999996, - "spec/models/gpo_confirmation_code_spec.rb": 0.163433899, - "spec/models/in_person_enrollment_spec.rb": 1.080877239, - "spec/models/null_identity_spec.rb": 0.013323827, - "spec/models/otp_requests_tracker_spec.rb": 0.066435966, - "spec/models/phone_configuration_spec.rb": 0.136422243, - "spec/models/phone_number_opt_out_spec.rb": 0.139528466, - "spec/models/profile_spec.rb": 1.144879759, - "spec/models/service_provider_identity_spec.rb": 0.526990577, - "spec/models/service_provider_spec.rb": 0.092240207, - "spec/models/sp_return_log_spec.rb": 0.008982879, - "spec/models/user_spec.rb": 2.169601104, - "spec/models/webauthn_configuration_spec.rb": 0.179146562, - "spec/policies/backup_code_policy_spec.rb": 0.041491402, - "spec/policies/service_provider_mfa_policy_spec.rb": 1.167226007, - "spec/policies/two_factor_authentication/piv_cac_policy_spec.rb": 0.097554552, - "spec/policies/user_mfa_policy_spec.rb": 0.413726253, - "spec/policies/webauthn_login_option_policy_spec.rb": 0.078762105, - "spec/presenters/account_reset/pending_presenter_spec.rb": 0.131198146, - "spec/presenters/account_show_presenter_spec.rb": 0.239898794, - "spec/presenters/additional_mfa_required_presenter_spec.rb": 0.209258848, - "spec/presenters/cancellation_presenter_spec.rb": 0.041558194, - "spec/presenters/completions_presenter_spec.rb": 0.702209128, - "spec/presenters/confirm_delete_email_presenter_spec.rb": 0.027691823, - "spec/presenters/eastern_time_presenter_spec.rb": 0.007232577, - "spec/presenters/fully_signed_in_modal_presenter_spec.rb": 0.0341076, - "spec/presenters/idv/cancellations_presenter_spec.rb": 0.047369415, - "spec/presenters/idv/gpo_presenter_spec.rb": 0.23047700299999999, - "spec/presenters/idv/in_person/ready_to_verify_presenter_spec.rb": 0.480756231, - "spec/presenters/idv/in_person/verification_results_email_presenter_spec.rb": 0.508174151, - "spec/presenters/image_upload_response_presenter_spec.rb": 0.100540624, - "spec/presenters/max_attempts_reached_presenter_spec.rb": 0.029343903, - "spec/presenters/mfa_confirmation_presenter_spec.rb": 0.077649707, - "spec/presenters/navigation_presenter_spec.rb": 0.121035086, - "spec/presenters/openid_connect_certs_presenter_spec.rb": 0.007293362, - "spec/presenters/openid_connect_configuration_presenter_spec.rb": 0.008804586, - "spec/presenters/openid_connect_user_info_presenter_spec.rb": 0.57471445, - "spec/presenters/partially_signed_in_modal_presenter_spec.rb": 0.036481249, - "spec/presenters/piv_cac_authentication_setup_presenter_spec.rb": 0.061132946, - "spec/presenters/piv_cac_error_presenter_spec.rb": 0.032818318, - "spec/presenters/risc_configuration_presenter_spec.rb": 0.010231914, - "spec/presenters/saml_request_presenter_spec.rb": 0.04651439, - "spec/presenters/setup_presenter_spec.rb": 0.08096505300000001, - "spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb": 0.028366371, - "spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb": 0.029430937, - "spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb": 0.007684023, - "spec/presenters/two_factor_auth_code/personal_key_presenter_spec.rb": 0.016164293, - "spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb": 0.062373953, - "spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb": 0.117779067, - "spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb": 0.197790952, - "spec/presenters/two_factor_authentication/auth_app_selection_presenter_spec.rb": 0.073220755, - "spec/presenters/two_factor_authentication/personal_key_selection_presenter_spec.rb": 0.007711227, - "spec/presenters/two_factor_authentication/phone_selection_presenter_spec.rb": 0.197122635, - "spec/presenters/two_factor_authentication/piv_cac_selection_presenter_spec.rb": 0.077580676, - "spec/presenters/two_factor_authentication/selection_presenter_spec.rb": 0.033361800999999996, - "spec/presenters/two_factor_authentication/sms_selection_presenter_spec.rb": 0.105708586, - "spec/presenters/two_factor_authentication/voice_selection_presenter_spec.rb": 0.104977153, - "spec/presenters/two_factor_authentication/webauthn_platform_selection_presenter_spec.rb": 0.094294125, - "spec/presenters/two_factor_authentication/webauthn_selection_presenter_spec.rb": 0.085759265, - "spec/presenters/two_factor_login_options_presenter_spec.rb": 0.126158202, - "spec/presenters/two_factor_options_presenter_spec.rb": 0.059798137, - "spec/presenters/utc_time_presenter_spec.rb": 0.007996935, - "spec/presenters/webauthn_setup_presenter_spec.rb": 0.224987129, - "spec/requests/acuant_sdk_spec.rb": 0.10719693899999999, - "spec/requests/api_cors_spec.rb": 0.648163585, - "spec/requests/csp_spec.rb": 0.1317261, - "spec/requests/headers_spec.rb": 0.289887932, - "spec/requests/i18n_spec.rb": 0.127211317, - "spec/requests/invalid_encoding_spec.rb": 0.316951142, - "spec/requests/invalid_sign_in_params_spec.rb": 0.100030549, - "spec/requests/not_acceptable_spec.rb": 0.091949009, - "spec/requests/openid_connect_authorize_spec.rb": 0.682524608, - "spec/requests/openid_connect_cors_spec.rb": 0.775186246, - "spec/requests/page_not_found_spec.rb": 0.321029426, - "spec/requests/params_is_string_instead_of_hash_spec.rb": 0.036043056, - "spec/requests/rack_attack_spec.rb": 7.035250818, - "spec/requests/redirects_spec.rb": 0.112060716, - "spec/requests/redis_down_spec.rb": 0.042421866, - "spec/requests/saml_requests_spec.rb": 0.126058405, - "spec/requests/secure_cookies_spec.rb": 0.317706497, - "spec/routing/gpo_verification_routing_spec.rb": 0.271897607, - "spec/scripts/changelog_check_spec.rb": 0.087690587, - "spec/services/access_token_verifier_spec.rb": 0.070778914, - "spec/services/account_reset/cancel_request_for_user_spec.rb": 0.522738329, - "spec/services/account_reset/cancel_spec.rb": 1.313280391, - "spec/services/account_reset/create_request_spec.rb": 0.5140622619999999, - "spec/services/account_reset/delete_account_spec.rb": 0.470135255, - "spec/services/account_reset/find_prending_request_for_user_spec.rb": 0.116338195, - "spec/services/account_reset/grant_request_spec.rb": 0.033515597, - "spec/services/account_reset/grant_requests_and_send_emails_spec.rb": 1.088987596, - "spec/services/account_reset/notify_user_of_request_cancellation_spec.rb": 0.609316665, - "spec/services/active_profile_encryptor_spec.rb": 0.045989857, - "spec/services/agency_identity_linker_spec.rb": 0.444096779, - "spec/services/agency_seeder_spec.rb": 0.06666127399999999, - "spec/services/agreements/iaa_gtc_seeder_spec.rb": 0.047563289, - "spec/services/agreements/iaa_order_seeder_spec.rb": 0.073733356, - "spec/services/agreements/integration_seeder_spec.rb": 0.077652525, - "spec/services/agreements/integration_status_seeder_spec.rb": 0.040083455000000004, - "spec/services/agreements/partner_account_seeder_spec.rb": 0.050986908, - "spec/services/agreements/partner_account_status_seeder_spec.rb": 0.057081632, - "spec/services/analytics_spec.rb": 0.196870647, - "spec/services/arcgis_api/geocoder_spec.rb": 0.254591012, - "spec/services/attribute_asserter_spec.rb": 1.417471838, - "spec/services/backup_code_generator_spec.rb": 1.608796853, - "spec/services/banned_user_resolver_spec.rb": 0.169445944, - "spec/services/barcode_outputter_spec.rb": 0.021030761, - "spec/services/browser_cache_spec.rb": 0.023664325, - "spec/services/calendar_service_spec.rb": 0.151204539, - "spec/services/cloud_front_header_parser_spec.rb": 0.021399525, - "spec/services/completions_decider_spec.rb": 0.067696835, - "spec/services/database_health_checker_spec.rb": 0.021787039, - "spec/services/date_parser_spec.rb": 0.037518695, - "spec/services/db/add_document_verification_and_selfie_costs_spec.rb": 0.042545368, - "spec/services/db/identity/sp_active_user_counts_spec.rb": 0.076766587, - "spec/services/db/identity/sp_user_counts_spec.rb": 0.035604365, - "spec/services/db/monthly_auth_count/total_monthly_auth_counts_spec.rb": 0.057617347, - "spec/services/db/monthly_sp_auth_count/total_monthly_auth_counts_within_iaa_window_spec.rb": 0.118074815, - "spec/services/db/monthly_sp_auth_count/unique_monthly_auth_counts_by_iaa_spec.rb": 0.17832504100000002, - "spec/services/db/sp_return_log_spec.rb": 0.017495576, - "spec/services/deleted_accounts_report_spec.rb": 0.168865146, - "spec/services/displayable_pii_formatter_spec.rb": 0.877241402, - "spec/services/doc_auth/acuant/acuant_client_spec.rb": 0.595169502, - "spec/services/doc_auth/acuant/pii_from_doc_spec.rb": 0.039825227, - "spec/services/doc_auth/acuant/request_spec.rb": 0.5969638580000001, - "spec/services/doc_auth/acuant/requests/create_document_request_spec.rb": 0.052855897, - "spec/services/doc_auth/acuant/requests/get_results_request_spec.rb": 0.057867368, - "spec/services/doc_auth/acuant/requests/upload_image_request_spec.rb": 0.04673682, - "spec/services/doc_auth/acuant/responses/create_document_response_spec.rb": 0.008015186, - "spec/services/doc_auth/acuant/responses/get_results_response_spec.rb": 0.11955101, - "spec/services/doc_auth/acuant/result_codes_spec.rb": 0.015067951, - "spec/services/doc_auth/error_generator_spec.rb": 0.173362816, - "spec/services/doc_auth/lexis_nexis/lexis_nexis_client_spec.rb": 0.339152287, - "spec/services/doc_auth/lexis_nexis/request_spec.rb": 0.513154343, - "spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb": 0.08492709600000001, - "spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb": 0.526238586, - "spec/services/doc_auth/mock/dock_auth_mock_client_spec.rb": 0.089377347, - "spec/services/doc_auth/mock/result_response_spec.rb": 0.130995995, - "spec/services/doc_auth/processed_alert_to_log_alert_formatter_spec.rb": 0.048100604, - "spec/services/doc_auth/response_spec.rb": 0.110714172, - "spec/services/doc_auth_router_spec.rb": 0.104369947, - "spec/services/document_capture_session_async_result_spec.rb": 0.04468306, - "spec/services/document_capture_session_result_spec.rb": 0.013961617, - "spec/services/duration_parser_spec.rb": 0.07542789899999999, - "spec/services/email_confirmation_token_validator_spec.rb": 0.171470942, - "spec/services/encrypted_attribute_spec.rb": 0.045735786, - "spec/services/encrypted_document_storage/document_writer_spec.rb": 0.056115484, - "spec/services/encrypted_document_storage/local_storage_spec.rb": 0.006655648, - "spec/services/encrypted_document_storage/s3_storage_spec.rb": 0.011837611, - "spec/services/encrypted_redis_struct_storage_spec.rb": 0.095256547, - "spec/services/encryption/aes_cipher_spec.rb": 0.029692198, - "spec/services/encryption/contextless_kms_client_spec.rb": 0.158337668, - "spec/services/encryption/encryptors/aes_encryptor_spec.rb": 0.021458273, - "spec/services/encryption/encryptors/attribute_encryptor_spec.rb": 0.056215738, - "spec/services/encryption/encryptors/background_proofing_arg_encryptor_spec.rb": 0.032815329, - "spec/services/encryption/encryptors/pii_encryptor_spec.rb": 0.087434982, - "spec/services/encryption/encryptors/session_encryptor_spec.rb": 0.022074273, - "spec/services/encryption/kms_client_spec.rb": 0.124088057, - "spec/services/encryption/kms_logger_spec.rb": 0.027827677000000002, - "spec/services/encryption/multi_region_kms_client_spec.rb": 0.083713106, - "spec/services/encryption/password_verifier_spec.rb": 0.121817937, - "spec/services/encryption/uak_password_verifier_spec.rb": 0.290313061, - "spec/services/encryption/user_access_key_spec.rb": 0.082614757, - "spec/services/event_disavowal/disavow_event_spec.rb": 0.029016975, - "spec/services/event_disavowal/find_disavowed_event_spec.rb": 0.078908649, - "spec/services/event_disavowal/validate_disavowed_event_spec.rb": 0.101543407, - "spec/services/forget_all_browsers_spec.rb": 0.020601921, - "spec/services/form_response_spec.rb": 0.229848636, - "spec/services/frontend_logger_spec.rb": 0.03759352, - "spec/services/funnel/registration/add_mfa_spec.rb": 0.030655013, - "spec/services/funnel/registration/total_registered_count_spec.rb": 2.41516231, - "spec/services/gpo_confirmation_exporter_spec.rb": 0.014903229, - "spec/services/gpo_confirmation_maker_spec.rb": 0.201290419, - "spec/services/gpo_confirmation_spec.rb": 0.023151908, - "spec/services/gpo_confirmation_uploader_spec.rb": 0.085924338, - "spec/services/gpo_daily_test_sender_spec.rb": 0.069554278, - "spec/services/health_check_summary_spec.rb": 0.01377171, - "spec/services/iaa_reporting_helper_spec.rb": 0.197426729, - "spec/services/ial_context_spec.rb": 0.564007904, - "spec/services/id_token_builder_spec.rb": 0.44178926, - "spec/services/identity_linker_spec.rb": 0.34482297, - "spec/services/idv/actions/verify_document_status_action_spec.rb": 0.310872931, - "spec/services/idv/agent_spec.rb": 0.431849365, - "spec/services/idv/analytics_events_enhancer_spec.rb": 0.11322181099999999, - "spec/services/idv/cancel_verification_attempt_spec.rb": 0.250370914, - "spec/services/idv/data_url_image_spec.rb": 0.02930365, - "spec/services/idv/doc_auth_form_response_spec.rb": 0.047722898, - "spec/services/idv/duplicate_ssn_finder_spec.rb": 0.396549523, - "spec/services/idv/gpo_mail_spec.rb": 0.140903219, - "spec/services/idv/in_person/completion_survey_sender_spec.rb": 0.701078816, - "spec/services/idv/in_person/enrollment_code_formatter_spec.rb": 0.007275535, - "spec/services/idv/in_person_config_spec.rb": 0.09524637500000001, - "spec/services/idv/inherited_proofing/service_provider_forms_spec.rb": 0.017866026, - "spec/services/idv/inherited_proofing/service_provider_services_spec.rb": 0.046329605999999995, - "spec/services/idv/inherited_proofing/va/mocks/service_spec.rb": 0.022656277, - "spec/services/idv/inherited_proofing/va/service_spec.rb": 0.212388074, - "spec/services/idv/phone_confirmation_session_spec.rb": 0.072848268, - "spec/services/idv/phone_step_spec.rb": 0.892131694, - "spec/services/idv/profile_maker_spec.rb": 0.216338542, - "spec/services/idv/proofing_components_logging_spec.rb": 0.00808737, - "spec/services/idv/send_phone_confirmation_otp_spec.rb": 0.165591839, - "spec/services/idv/session_spec.rb": 1.022631924, - "spec/services/idv/steps/document_capture_step_spec.rb": 0.11231677100000001, - "spec/services/idv/steps/in_person/address_step_spec.rb": 0.034563412, - "spec/services/idv/steps/in_person/ssn_step_spec.rb": 0.10425285200000001, - "spec/services/idv/steps/in_person/state_id_step_spec.rb": 0.124847793, - "spec/services/idv/steps/in_person/verify_step_spec.rb": 0.423742355, - "spec/services/idv/steps/in_person/verify_wait_step_show_spec.rb": 0.23411788, - "spec/services/idv/steps/send_link_step_spec.rb": 0.03121841, - "spec/services/idv/steps/ssn_step_spec.rb": 0.147318598, - "spec/services/idv/steps/verify_step_spec.rb": 0.221633626, - "spec/services/idv/steps/verify_wait_step_show_spec.rb": 0.24069390899999998, - "spec/services/idv/steps/welcome_step_spec.rb": 0.096048725, - "spec/services/image_upload_presigned_url_generator_spec.rb": 0.021236647, - "spec/services/irs_attempts_api/attempt_event_spec.rb": 1.029090904, - "spec/services/irs_attempts_api/envelope_encryptor_spec.rb": 1.867925429, - "spec/services/irs_attempts_api/redis_client_spec.rb": 0.02766815, - "spec/services/irs_attempts_api/tracker_spec.rb": 0.426418519, - "spec/services/key_rotator/attribute_encryption_spec.rb": 0.047732795999999994, - "spec/services/key_rotator/hmac_fingerprinter_spec.rb": 0.144293607, - "spec/services/maintenance_window_spec.rb": 0.055038656000000005, - "spec/services/marketing_site_spec.rb": 0.146001812, - "spec/services/multi_health_checker_spec.rb": 0.019880269, - "spec/services/openid_connect_attribute_scoper_spec.rb": 0.119872429, - "spec/services/otp_preference_updater_spec.rb": 0.062165502, - "spec/services/otp_rate_limiter_spec.rb": 0.152157655, - "spec/services/out_of_band_session_accessor_spec.rb": 0.062152927, - "spec/services/outbound_health_checker_spec.rb": 0.184794702, - "spec/services/parse_controller_from_referer_spec.rb": 0.016213323, - "spec/services/personal_key_generator_spec.rb": 0.39597725500000003, - "spec/services/phone_formatter_spec.rb": 0.044494338, - "spec/services/phone_number_capabilities_spec.rb": 0.311111883, - "spec/services/pii/attributes_spec.rb": 0.048974035, - "spec/services/pii/cacher_spec.rb": 0.5165822369999999, - "spec/services/pii/fingerprinter_spec.rb": 0.088296894, - "spec/services/pii/re_encryptor_spec.rb": 0.13912480900000002, - "spec/services/piv_cac/check_config_spec.rb": 0.023588783, - "spec/services/piv_cac_service_spec.rb": 0.151519679, - "spec/services/profanity_detector_spec.rb": 0.055579421, - "spec/services/proofing/aamva/applicant_spec.rb": 0.022719184, - "spec/services/proofing/aamva/authentication_client_spec.rb": 0.226609978, - "spec/services/proofing/aamva/hmac_secret_spec.rb": 0.036037677, - "spec/services/proofing/aamva/proofing_spec.rb": 0.222116563, - "spec/services/proofing/aamva/request/authentication_token_request_spec.rb": 0.331372834, - "spec/services/proofing/aamva/request/security_token_request_spec.rb": 1.695364844, - "spec/services/proofing/aamva/request/verification_request_spec.rb": 0.5182939329999999, - "spec/services/proofing/aamva/response/authentication_token_response_spec.rb": 0.056526948, - "spec/services/proofing/aamva/response/security_token_response_spec.rb": 0.089254431, - "spec/services/proofing/aamva/response/verification_response_spec.rb": 0.295524163, - "spec/services/proofing/aamva/soap_error_handler_spec.rb": 0.062254464, - "spec/services/proofing/aamva/verification_client_spec.rb": 0.521299008, - "spec/services/proofing/lexis_nexis/date_formatter_spec.rb": 0.024901723, - "spec/services/proofing/lexis_nexis/ddp/proofing_spec.rb": 0.07044550599999999, - "spec/services/proofing/lexis_nexis/ddp/response_redacter_spec.rb": 0.029369222, - "spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb": 0.154966442, - "spec/services/proofing/lexis_nexis/instant_verify/check_to_attribute_mapper_spec.rb": 0.054316685, - "spec/services/proofing/lexis_nexis/instant_verify/proofing_spec.rb": 0.140340471, - "spec/services/proofing/lexis_nexis/instant_verify/verification_request_spec.rb": 0.118398591, - "spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb": 0.162158531, - "spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb": 0.103770414, - "spec/services/proofing/lexis_nexis/response_spec.rb": 0.114928848, - "spec/services/proofing/lexis_nexis/verification_error_parser_spec.rb": 0.032426917, - "spec/services/proofing/mock/address_mock_client_spec.rb": 0.033923987, - "spec/services/proofing/mock/ddp_mock_client_spec.rb": 0.047904126, - "spec/services/proofing/mock/device_profiling_backend_spec.rb": 0.015433675, - "spec/services/proofing/mock/resolution_mock_client_spec.rb": 0.068481, - "spec/services/proofing/mock/state_id_mock_client_spec.rb": 0.036386154, - "spec/services/proofing/resolution_result_adjudicator_spec.rb": 0.041358236, - "spec/services/proofing/result_spec.rb": 0.170110073, - "spec/services/proofing_session_async_result_spec.rb": 0.008156238, - "spec/services/push_notification/account_purged_event_spec.rb": 0.033696301, - "spec/services/push_notification/email_changed_event_spec.rb": 0.030610602, - "spec/services/push_notification/http_push_spec.rb": 0.658764272, - "spec/services/push_notification/identifier_recycled_event_spec.rb": 0.033834444000000005, - "spec/services/push_notification/mfa_limit_account_locked_event_spec.rb": 0.071419766, - "spec/services/push_notification/password_reset_event_spec.rb": 0.027089673, - "spec/services/push_notification/recovery_activated_event_spec.rb": 0.034678147, - "spec/services/push_notification/reproof_completed_event_spec.rb": 0.031385264999999996, - "spec/services/pwned_passwords/lookup_password_spec.rb": 0.017572236, - "spec/services/random_phrase_spec.rb": 0.054952944999999996, - "spec/services/reactivate_account_session_spec.rb": 0.145035185, - "spec/services/redis_rate_limiter_spec.rb": 0.06365961, - "spec/services/remember_device_cookie_spec.rb": 0.16358788600000002, - "spec/services/request_password_reset_spec.rb": 3.84982235, - "spec/services/reset_user_password_spec.rb": 1.656120346, - "spec/services/revoke_service_provider_consent_spec.rb": 0.037012274, - "spec/services/saml_endpoint_spec.rb": 0.077398775, - "spec/services/saml_request_validator_spec.rb": 0.129392116, - "spec/services/secure_headers_allow_list_spec.rb": 0.045560798, - "spec/services/send_sign_up_email_confirmation_spec.rb": 0.9002513870000001, - "spec/services/service_provider_request_proxy_spec.rb": 0.067697158, - "spec/services/service_provider_seeder_spec.rb": 1.489102754, - "spec/services/service_provider_updater_spec.rb": 0.371044052, - "spec/services/session_encryptor_spec.rb": 0.026784901, - "spec/services/sp_return_url_resolver_spec.rb": 0.087292078, - "spec/services/ssn_formatter_spec.rb": 0.071836666, - "spec/services/store_sp_metadata_in_session_spec.rb": 0.037607598, - "spec/services/throttle_spec.rb": 0.16150151399999998, - "spec/services/time_service_spec.rb": 0.007492697, - "spec/services/update_user_spec.rb": 0.315131791, - "spec/services/uri_service_spec.rb": 0.049602692000000004, - "spec/services/user_alerts/alert_user_about_account_verified_spec.rb": 0.644675534, - "spec/services/user_alerts/alert_user_about_new_device_spec.rb": 0.43763708, - "spec/services/user_alerts/alert_user_about_password_change_spec.rb": 0.45879819, - "spec/services/user_alerts/alert_user_about_personal_key_sign_in_spec.rb": 0.434650409, - "spec/services/user_event_creator_spec.rb": 0.25354719400000003, - "spec/services/user_seeder_spec.rb": 2.879392347, - "spec/services/user_session_context_spec.rb": 0.056569425, - "spec/services/usps_in_person_proofing/enrollment_helper_spec.rb": 1.713073552, - "spec/services/usps_in_person_proofing/proofer_spec.rb": 0.195618458, - "spec/services/uuid_reporter_spec.rb": 0.259169016, - "spec/services/vendor_status_spec.rb": 0.196496469, - "spec/services/x509/attribute_spec.rb": 0.007711406, - "spec/services/x509/attributes_spec.rb": 0.055760618, - "spec/svg_spec.rb": 0.997822538, - "spec/views/account_reset/cancel/show.html.erb_spec.rb": 0.02643611, - "spec/views/account_reset/confirm_delete_account/show.html.erb_spec.rb": 0.037357889000000005, - "spec/views/account_reset/confirm_request/show.html.erb_spec.rb": 0.020924519, - "spec/views/account_reset/delete_account/show.html.erb_spec.rb": 0.027562486, - "spec/views/account_reset/recovery_options/show.html.erb_spec.rb": 0.035277194, - "spec/views/account_reset/request/show.html.erb_spec.rb": 0.08116884499999999, - "spec/views/account_reset/user_mailer/email_confirmation_instructions.html.erb_spec.rb": 0.195369038, - "spec/views/account_reset/user_mailer/unconfirmed_email_instructions.html.erb_spec.rb": 0.14238541400000002, - "spec/views/accounts/_nav_auth.html.erb_spec.rb": 0.099497997, - "spec/views/accounts/connected_accounts/show.html.erb_spec.rb": 0.088269719, - "spec/views/accounts/history/show.html.erb_spec.rb": 0.04057107, - "spec/views/accounts/show.html.erb_spec.rb": 0.792754118, - "spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb": 0.28162121, - "spec/views/devise/passwords/edit.html.erb_spec.rb": 0.177408905, - "spec/views/devise/passwords/new.html.erb_spec.rb": 0.121723631, - "spec/views/devise/sessions/new.html.erb_spec.rb": 0.420667595, - "spec/views/devise/shared/_password_strength.html.erb_spec.rb": 0.077075795, - "spec/views/forgot_password/show.html.erb_spec.rb": 0.07941247900000001, - "spec/views/idv/activated.html.erb_spec.rb": 0.02576063, - "spec/views/idv/cancellations/destroy.html.erb_spec.rb": 0.043809009999999995, - "spec/views/idv/cancellations/new.html.erb_spec.rb": 0.096707272, - "spec/views/idv/come_back_later/show.html.erb_spec.rb": 0.066434787, - "spec/views/idv/doc_auth/_cancel.html.erb_spec.rb": 0.028245101999999998, - "spec/views/idv/doc_auth/upload.html.erb_spec.rb": 0.016437345, - "spec/views/idv/doc_auth/welcome.html.erb_spec.rb": 0.172654326, - "spec/views/idv/gpo/index.html.erb_spec.rb": 0.190390253, - "spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb": 0.422239303, - "spec/views/idv/inherited_proofing/agreement.html.erb_spec.rb": 0.054860779, - "spec/views/idv/inherited_proofing/get_started.html.erb_spec.rb": 0.073224101, - "spec/views/idv/inherited_proofing/retrieval.html.erb_spec.rb": 0.063003193, - "spec/views/idv/otp_delivery_method/new.html.erb_spec.rb": 2.346517686, - "spec/views/idv/phone/new.html.erb_spec.rb": 0.117593909, - "spec/views/idv/phone_errors/_warning.html.erb_spec.rb": 0.148965295, - "spec/views/idv/phone_errors/failure.html.erb_spec.rb": 0.035213686, - "spec/views/idv/phone_errors/jobfail.html.erb_spec.rb": 0.071391794, - "spec/views/idv/phone_errors/timeout.html.erb_spec.rb": 0.091650747, - "spec/views/idv/phone_errors/warning.html.erb_spec.rb": 2.491938882, - "spec/views/idv/review/new.html.erb_spec.rb": 0.228347029, - "spec/views/idv/session_errors/exception.html.erb_spec.rb": 0.036973738, - "spec/views/idv/session_errors/failure.html.erb_spec.rb": 0.032551618000000004, - "spec/views/idv/session_errors/throttled.html.erb_spec.rb": 0.066645808, - "spec/views/idv/session_errors/warning.html.erb_spec.rb": 0.078130013, - "spec/views/idv/setup_errors/show.html.erb_spec.rb": 0.027011202999999998, - "spec/views/idv/shared/_back.html.erb_spec.rb": 0.140458106, - "spec/views/idv/shared/_document_capture.html.erb_spec.rb": 0.163371821, - "spec/views/idv/shared/_error.html.erb_spec.rb": 0.417349375, - "spec/views/idv/shared/_ssn.html.erb_spec.rb": 0.20486670199999998, - "spec/views/layouts/application.html.erb_spec.rb": 0.514892659, - "spec/views/layouts/user_mailer.html.erb_spec.rb": 0.363813026, - "spec/views/mfa_confirmation/show.html.erb_spec.rb": 0.142166368, - "spec/views/partials/multi_factor_authentication/_mfa_selection.html.erb_spec.rb": 0.183879445, - "spec/views/phone_setup/index.html.erb_spec.rb": 0.30427586100000004, - "spec/views/reactivate_account/index.html.erb_spec.rb": 0.022433174, - "spec/views/shared/_address.html.erb_spec.rb": 0.042828727999999996, - "spec/views/shared/_banner.html.erb_spec.rb": 0.037668518, - "spec/views/shared/_email_languages.html.erb_spec.rb": 0.1201203, - "spec/views/shared/_footer_lite.html.erb_spec.rb": 0.126087999, - "spec/views/shared/_maintenance_window_alert.html.erb_spec.rb": 0.055376061000000004, - "spec/views/shared/_masked_text.html.erb_spec.rb": 0.09122343699999999, - "spec/views/shared/_nav_branded.html.erb_spec.rb": 0.08085326, - "spec/views/shared/_nav_lite.html.erb_spec.rb": 0.02417347, - "spec/views/shared/_personal_key.html.erb_spec.rb": 0.026055194, - "spec/views/shared/_troubleshooting_options.html.erb_spec.rb": 0.17557002300000002, - "spec/views/shared/personal_key/_key.html.erb_spec.rb": 0.023120367, - "spec/views/sign_up/completions/show.html.erb_spec.rb": 0.221683159, - "spec/views/sign_up/email_resend/new.html.erb_spec.rb": 0.035517566, - "spec/views/sign_up/emails/show.html.erb_spec.rb": 0.04764852, - "spec/views/sign_up/passwords/new.html.erb_spec.rb": 0.105079434, - "spec/views/sign_up/registrations/new.html.erb_spec.rb": 0.164365077, - "spec/views/two_factor_authentication/options/index.html.erb_spec.rb": 0.159048591, - "spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb": 0.39991023600000003, - "spec/views/two_factor_authentication/personal_key_verification/show.html.erb_spec.rb": 0.154182877, - "spec/views/two_factor_authentication/sms_opt_in/error.html.erb_spec.rb": 0.116736438, - "spec/views/two_factor_authentication/sms_opt_in/new.html.erb_spec.rb": 0.090751205, - "spec/views/two_factor_authentication/totp_verification/show.html.erb_spec.rb": 0.273594457, - "spec/views/users/backup_code_setup/create.html.erb_spec.rb": 0.284494432, - "spec/views/users/backup_code_setup/reminder.html.erb_spec.rb": 0.055875805, - "spec/views/users/delete/show.html.erb_spec.rb": 0.25852063200000003, - "spec/views/users/edit_phone/remove_phone.html.erb_spec.rb": 0.11129446900000001, - "spec/views/users/passwords/edit.html.erb_spec.rb": 0.12145905800000001, - "spec/views/users/phones/add.html.erb_spec.rb": 0.070481236, - "spec/views/users/piv_cac_authentication_setup/new.html.erb_spec.rb": 0.06872613699999999, - "spec/views/users/shared/_otp_delivery_preference_selection.html.erb_spec.rb": 0.146298796, - "spec/views/users/totp_setup/new.html.erb_spec.rb": 0.204829306, - "spec/views/users/two_factor_authentication_setup/index.html.erb_spec.rb": 0.16885018000000002, - "spec/views/users/webauthn_setup/new.html.erb_spec.rb": 0.032509383, - "spec/views/vendor_outage/show.html.erb_spec.rb": 2.786345259 -} \ No newline at end of file + "spec/bin/query-cloudwatch_spec.rb": 0.185691175, + "spec/browsers_json_spec.rb": 0.01345417, + "spec/components/accordion_component_spec.rb": 0.047253322, + "spec/components/alert_component_spec.rb": 0.101356174, + "spec/components/alert_icon_component_spec.rb": 0.050707798, + "spec/components/barcode_component_spec.rb": 0.102853523, + "spec/components/base_component_spec.rb": 0.065462929, + "spec/components/block_link_component_spec.rb": 0.033560085, + "spec/components/button_component_spec.rb": 0.081012837, + "spec/components/captcha_submit_button_component_spec.rb": 0.115090254, + "spec/components/click_observer_component_spec.rb": 0.019846901, + "spec/components/clipboard_button_component_spec.rb": 0.026499913, + "spec/components/countdown_alert_component_spec.rb": 0.066877983, + "spec/components/countdown_component_spec.rb": 0.039926967, + "spec/components/download_button_component_spec.rb": 0.028188849000000002, + "spec/components/flash_component_spec.rb": 0.029677084, + "spec/components/form_link_component_spec.rb": 0.018150979, + "spec/components/icon_component_spec.rb": 0.037456068, + "spec/components/javascript_required_component_spec.rb": 0.068863599, + "spec/components/language_picker_component_spec.rb": 0.043185727, + "spec/components/memorable_date_component_spec.rb": 0.189257412, + "spec/components/modal_component_spec.rb": 0.057037631, + "spec/components/one_time_code_input_component_spec.rb": 0.115337998, + "spec/components/page_footer_component_spec.rb": 0.023401319, + "spec/components/page_heading_component_spec.rb": 0.030338535, + "spec/components/password_toggle_component_spec.rb": 0.059668459, + "spec/components/phone_input_component_spec.rb": 0.627761231, + "spec/components/print_button_component_spec.rb": 0.018293783, + "spec/components/process_list_component_spec.rb": 0.058629781, + "spec/components/spinner_button_component_spec.rb": 0.052304052, + "spec/components/status_page_component_spec.rb": 0.073577429, + "spec/components/step_indicator_component_spec.rb": 0.102082847, + "spec/components/step_indicator_step_component_spec.rb": 0.06419309299999999, + "spec/components/submit_button_component_spec.rb": 0.032022745, + "spec/components/time_component_spec.rb": 0.037802394, + "spec/components/troubleshooting_options_component_spec.rb": 0.0640858, + "spec/components/validated_field_component_spec.rb": 0.093702209, + "spec/components/vendor_outage_alert_component_spec.rb": 0.050706314, + "spec/config/initializers/ab_tests_spec.rb": 0.010122217, + "spec/config/initializers/ahoy_spec.rb": 0.045401162, + "spec/config/initializers/async_exception_spec.rb": 0.014906909, + "spec/config/initializers/ext_digest_spec.rb": 0.011389408, + "spec/config/initializers/job_configurations_spec.rb": 0.064500894, + "spec/config/initializers/phonelib_spec.rb": 0.008330456, + "spec/config/initializers/secure_headers_spec.rb": 0.007119445, + "spec/controllers/account_reset/cancel_controller_spec.rb": 0.719626954, + "spec/controllers/account_reset/confirm_delete_account_controller_spec.rb": 0.029051145, + "spec/controllers/account_reset/confirm_request_controller_spec.rb": 0.028675140000000002, + "spec/controllers/account_reset/delete_account_controller_spec.rb": 0.660295251, + "spec/controllers/account_reset/pending_controller_spec.rb": 0.29364802100000004, + "spec/controllers/account_reset/recovery_options_controller_spec.rb": 0.127795112, + "spec/controllers/account_reset/request_controller_spec.rb": 1.588041571, + "spec/controllers/accounts/personal_keys_controller_spec.rb": 0.571222531, + "spec/controllers/accounts_controller_spec.rb": 0.24805039999999998, + "spec/controllers/analytics_events_controller_spec.rb": 0.02453217, + "spec/controllers/api/internal/sessions_controller_spec.rb": 0.27534024, + "spec/controllers/api/irs_attempts_api_controller_spec.rb": 0.619138087, + "spec/controllers/api/verify/base_controller_spec.rb": 0.12245304000000001, + "spec/controllers/api/verify/document_capture_controller_spec.rb": 0.241268252, + "spec/controllers/api/verify/document_capture_errors_controller_spec.rb": 0.17630573, + "spec/controllers/application_controller_spec.rb": 1.687593063, + "spec/controllers/concerns/api/csrf_token_concern_spec.rb": 0.01633232, + "spec/controllers/concerns/effective_user_spec.rb": 0.0690955, + "spec/controllers/concerns/idv/document_capture_concern_spec.rb": 0.032767786, + "spec/controllers/concerns/idv/phone_otp_rate_limitable_spec.rb": 0.028521787, + "spec/controllers/concerns/idv/step_indicator_concern_spec.rb": 0.282383953, + "spec/controllers/concerns/idv/threat_metrix_concern_spec.rb": 0.062069293, + "spec/controllers/concerns/idv_step_concern_spec.rb": 0.259207908, + "spec/controllers/concerns/reauthentication_required_concern_spec.rb": 0.214750925, + "spec/controllers/concerns/recaptcha_concern_spec.rb": 0.06723346499999999, + "spec/controllers/concerns/render_condition_concern_spec.rb": 0.099476305, + "spec/controllers/concerns/verify_sp_attributes_concern_spec.rb": 0.529739826, + "spec/controllers/country_support_controller_spec.rb": 0.048648460000000004, + "spec/controllers/event_disavowal_controller_spec.rb": 0.313231184, + "spec/controllers/fake_s3_controller_spec.rb": 0.033832763, + "spec/controllers/forgot_password_controller_spec.rb": 0.023822617, + "spec/controllers/frontend_log_controller_spec.rb": 0.31407554, + "spec/controllers/health/database_controller_spec.rb": 0.044527772, + "spec/controllers/health/health_controller_spec.rb": 0.037807157, + "spec/controllers/health/outbound_controller_spec.rb": 0.076502189, + "spec/controllers/idv/cancellations_controller_spec.rb": 0.698957932, + "spec/controllers/idv/capture_doc_controller_spec.rb": 0.22255448, + "spec/controllers/idv/capture_doc_status_controller_spec.rb": 0.404447052, + "spec/controllers/idv/come_back_later_controller_spec.rb": 0.061399015, + "spec/controllers/idv/doc_auth_controller_spec.rb": 0.7707043379999999, + "spec/controllers/idv/document_capture_controller_spec.rb": 0.32428111, + "spec/controllers/idv/forgot_password_controller_spec.rb": 0.28287656099999997, + "spec/controllers/idv/gpo_controller_spec.rb": 1.037941323, + "spec/controllers/idv/gpo_verify_controller_spec.rb": 1.390012413, + "spec/controllers/idv/image_uploads_controller_spec.rb": 1.319783393, + "spec/controllers/idv/in_person/address_search_controller_spec.rb": 0.22545855199999998, + "spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb": 0.180886167, + "spec/controllers/idv/in_person/usps_locations_controller_spec.rb": 0.450196962, + "spec/controllers/idv/in_person/verify_info_controller_spec.rb": 0.477626004, + "spec/controllers/idv/in_person_controller_spec.rb": 0.402102335, + "spec/controllers/idv/not_verified_controller_spec.rb": 0.036425387, + "spec/controllers/idv/otp_verification_controller_spec.rb": 0.244264492, + "spec/controllers/idv/personal_key_controller_spec.rb": 1.8358741619999999, + "spec/controllers/idv/phone_controller_spec.rb": 1.566826773, + "spec/controllers/idv/phone_errors_controller_spec.rb": 0.953970671, + "spec/controllers/idv/please_call_controller_spec.rb": 0.084055039, + "spec/controllers/idv/resend_otp_controller_spec.rb": 0.115737306, + "spec/controllers/idv/review_controller_spec.rb": 5.810555222, + "spec/controllers/idv/session_errors_controller_spec.rb": 1.489943436, + "spec/controllers/idv/sessions_controller_spec.rb": 0.316428684, + "spec/controllers/idv/ssn_controller_spec.rb": 0.501697239, + "spec/controllers/idv/unavailable_controller_spec.rb": 0.106648676, + "spec/controllers/idv/verify_info_controller_spec.rb": 0.896669286, + "spec/controllers/idv_controller_spec.rb": 0.432986416, + "spec/controllers/mfa_confirmation_controller_spec.rb": 0.295023469, + "spec/controllers/no_js_controller_spec.rb": 0.032611193, + "spec/controllers/openid_connect/authorization_controller_spec.rb": 1.303357646, + "spec/controllers/openid_connect/certs_controller_spec.rb": 0.024612279, + "spec/controllers/openid_connect/configuration_controller_spec.rb": 0.022582953000000003, + "spec/controllers/openid_connect/logout_controller_spec.rb": 1.126554493, + "spec/controllers/openid_connect/token_controller_spec.rb": 0.196184474, + "spec/controllers/openid_connect/user_info_controller_spec.rb": 0.176545058, + "spec/controllers/pages_controller_spec.rb": 0.07820936, + "spec/controllers/password_capture_controller_spec.rb": 0.10005012299999999, + "spec/controllers/reactivate_account_controller_spec.rb": 0.153951747, + "spec/controllers/redirect/contact_controller_spec.rb": 0.028664921, + "spec/controllers/redirect/help_center_controller_spec.rb": 0.08361238, + "spec/controllers/redirect/policy_controller_spec.rb": 0.015941174, + "spec/controllers/redirect/return_to_sp_controller_spec.rb": 0.100976527, + "spec/controllers/risc/configuration_controller_spec.rb": 0.014231443, + "spec/controllers/risc/security_events_controller_spec.rb": 0.598680498, + "spec/controllers/saml_completion_controller_spec.rb": 0.04809786, + "spec/controllers/saml_idp_controller_spec.rb": 10.502273236, + "spec/controllers/saml_post_controller_spec.rb": 0.026114579, + "spec/controllers/saml_signed_message_spec.rb": 0.440552398, + "spec/controllers/service_provider_controller_spec.rb": 0.091931371, + "spec/controllers/sign_out_controller_spec.rb": 0.059610685000000004, + "spec/controllers/sign_up/cancellations_controller_spec.rb": 0.340604805, + "spec/controllers/sign_up/completions_controller_spec.rb": 0.716640834, + "spec/controllers/sign_up/email_confirmations_controller_spec.rb": 0.199802754, + "spec/controllers/sign_up/emails_controller_spec.rb": 0.020562414, + "spec/controllers/sign_up/passwords_controller_spec.rb": 0.153737338, + "spec/controllers/sign_up/registrations_controller_spec.rb": 1.003662417, + "spec/controllers/test/device_profiling_controller_spec.rb": 0.029727224, + "spec/controllers/test/piv_cac_authentication_test_subject_controller_spec.rb": 0.066411684, + "spec/controllers/test/push_notification_controller_spec.rb": 0.037275222, + "spec/controllers/test/telephony_controller_spec.rb": 0.034129193, + "spec/controllers/two_factor_authentication/backup_code_verification_controller_spec.rb": 0.617542073, + "spec/controllers/two_factor_authentication/options_controller_spec.rb": 0.386797851, + "spec/controllers/two_factor_authentication/otp_expired_controller_spec.rb": 0.27893086500000003, + "spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb": 2.893660196, + "spec/controllers/two_factor_authentication/personal_key_verification_controller_spec.rb": 0.824157819, + "spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb": 0.839421281, + "spec/controllers/two_factor_authentication/sms_opt_in_controller_spec.rb": 0.626459143, + "spec/controllers/two_factor_authentication/totp_verification_controller_spec.rb": 0.614282958, + "spec/controllers/two_factor_authentication/webauthn_verification_controller_spec.rb": 0.356852901, + "spec/controllers/users/authorization_confirmation_controller_spec.rb": 0.145457812, + "spec/controllers/users/backup_code_setup_controller_spec.rb": 2.125878679, + "spec/controllers/users/delete_controller_spec.rb": 0.600356157, + "spec/controllers/users/edit_phone_controller_spec.rb": 0.192218183, + "spec/controllers/users/email_confirmations_controller_spec.rb": 1.460684717, + "spec/controllers/users/email_language_controller_spec.rb": 0.212705772, + "spec/controllers/users/emails_controller_spec.rb": 0.586191456, + "spec/controllers/users/forget_all_browsers_controller_spec.rb": 0.184442002, + "spec/controllers/users/mfa_selection_controller_spec.rb": 0.332298961, + "spec/controllers/users/passwords_controller_spec.rb": 1.314142275, + "spec/controllers/users/personal_keys_controller_spec.rb": 0.306624967, + "spec/controllers/users/phone_setup_controller_spec.rb": 0.270372402, + "spec/controllers/users/phones_controller_spec.rb": 0.326525417, + "spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb": 0.770934656, + "spec/controllers/users/reset_passwords_controller_spec.rb": 2.195733769, + "spec/controllers/users/rules_of_use_controller_spec.rb": 0.42946418, + "spec/controllers/users/service_provider_revoke_controller_spec.rb": 0.402157388, + "spec/controllers/users/sessions_controller_spec.rb": 1.653365633, + "spec/controllers/users/totp_setup_controller_spec.rb": 3.959273352, + "spec/controllers/users/two_factor_authentication_controller_spec.rb": 1.941067333, + "spec/controllers/users/two_factor_authentication_setup_controller_spec.rb": 0.311643449, + "spec/controllers/users/verify_password_controller_spec.rb": 0.471758447, + "spec/controllers/users/verify_personal_key_controller_spec.rb": 0.774642238, + "spec/controllers/users/webauthn_setup_controller_spec.rb": 0.678193085, + "spec/controllers/vendor_outage_controller_spec.rb": 0.041163561, + "spec/decorators/device_decorator_spec.rb": 0.038917785999999996, + "spec/decorators/email_context_spec.rb": 0.050924115, + "spec/decorators/event_decorator_spec.rb": 0.110334059, + "spec/decorators/mfa_context_spec.rb": 0.909110676, + "spec/decorators/service_provider_session_decorator_spec.rb": 0.34307433, + "spec/decorators/session_decorator_spec.rb": 0.044499213, + "spec/decorators/user_decorator_spec.rb": 1.102677904, + "spec/features/accessibility/idv_pages_spec.rb": 165.375981848, + "spec/features/accessibility/static_pages_spec.rb": 110.254003334, + "spec/features/accessibility/user_pages_spec.rb": 285.58769503999997, + "spec/features/accessibility/visitor_pages_spec.rb": 63.034958193, + "spec/features/account/backup_codes_spec.rb": 17.914824163, + "spec/features/account/device_spec.rb": 3.196686714, + "spec/features/account/unphishable_badge_spec.rb": 6.3247692, + "spec/features/account_connected_apps_spec.rb": 6.903688739, + "spec/features/account_creation/multiple_browsers_spec.rb": 16.855496967, + "spec/features/account_creation/sp_return_log_spec.rb": 4.56924897, + "spec/features/account_email_language_spec.rb": 10.139567911, + "spec/features/account_history_spec.rb": 3.330966708, + "spec/features/account_reset/cancel_request_spec.rb": 4.739523311, + "spec/features/account_reset/delete_account_spec.rb": 17.839138194, + "spec/features/account_reset/pending_request_spec.rb": 5.006512109, + "spec/features/device_tracking_spec.rb": 6.473703227, + "spec/features/event_disavowal_spec.rb": 42.21487633, + "spec/features/ialmax/saml_sign_in_spec.rb": 27.437746482999998, + "spec/features/idv/account_creation_spec.rb": 51.378506317, + "spec/features/idv/actions/cancel_link_sent_action_spec.rb": 4.458647413, + "spec/features/idv/actions/redo_document_capture_action_spec.rb": 20.62188384, + "spec/features/idv/analytics_spec.rb": 27.190867567, + "spec/features/idv/cancel_spec.rb": 24.771272547, + "spec/features/idv/clearing_and_restarting_spec.rb": 69.552144226, + "spec/features/idv/doc_auth/address_step_spec.rb": 38.005462285, + "spec/features/idv/doc_auth/agreement_step_spec.rb": 26.433021312, + "spec/features/idv/doc_auth/document_capture_spec.rb": 51.291508331, + "spec/features/idv/doc_auth/document_capture_step_spec.rb": 50.414096529, + "spec/features/idv/doc_auth/email_sent_step_spec.rb": 3.80092589, + "spec/features/idv/doc_auth/link_sent_step_spec.rb": 4.283712103, + "spec/features/idv/doc_auth/ssn_step_spec.rb": 10.641385753, + "spec/features/idv/doc_auth/test_credentials_spec.rb": 9.99784889, + "spec/features/idv/doc_auth/upload_step_spec.rb": 51.863814609, + "spec/features/idv/doc_auth/verify_info_step_spec.rb": 123.835049723, + "spec/features/idv/doc_auth/welcome_step_spec.rb": 16.895202564999998, + "spec/features/idv/doc_capture/capture_complete_step_spec.rb": 5.132482017, + "spec/features/idv/doc_capture/document_capture_step_spec.rb": 68.258863793, + "spec/features/idv/gpo_disabled_spec.rb": 10.503912021, + "spec/features/idv/hybrid_flow_spec.rb": 23.181832478, + "spec/features/idv/in_person_spec.rb": 131.519424948, + "spec/features/idv/outage_spec.rb": 107.678846404, + "spec/features/idv/phone_input_spec.rb": 12.194582901, + "spec/features/idv/phone_otp_rate_limiting_spec.rb": 25.290264875, + "spec/features/idv/proofing_components_spec.rb": 44.572364813, + "spec/features/idv/sp_handoff_spec.rb": 127.326103007, + "spec/features/idv/sp_requested_attributes_spec.rb": 64.246955222, + "spec/features/idv/steps/confirmation_step_spec.rb": 34.876604572, + "spec/features/idv/steps/forgot_password_step_spec.rb": 21.736264375, + "spec/features/idv/steps/gpo_otp_verification_step_spec.rb": 89.080326171, + "spec/features/idv/steps/gpo_step_spec.rb": 51.650038832999996, + "spec/features/idv/steps/in_person/verify_info_spec.rb": 9.877270575, + "spec/features/idv/steps/in_person/verify_step_spec.rb": 24.78400964, + "spec/features/idv/steps/phone_otp_verification_step_spec.rb": 27.199549958, + "spec/features/idv/steps/phone_step_spec.rb": 195.75975706, + "spec/features/idv/steps/review_step_spec.rb": 44.360843345, + "spec/features/idv/threatmetrix_pending_spec.rb": 49.221405527, + "spec/features/idv/uak_password_spec.rb": 7.902013181, + "spec/features/irs_attempts_api/event_tracking_spec.rb": 25.354044524, + "spec/features/legacy_passwords_spec.rb": 13.563822508, + "spec/features/load_testing/email_sign_up_spec.rb": 3.467094116, + "spec/features/multi_factor_authentication/mfa_cta_spec.rb": 12.35607854, + "spec/features/multiple_emails/add_email_spec.rb": 58.379324834, + "spec/features/multiple_emails/email_management_spec.rb": 24.978637444, + "spec/features/multiple_emails/reset_password_spec.rb": 7.430474027, + "spec/features/multiple_emails/sign_in_spec.rb": 11.370547192, + "spec/features/multiple_emails/sp_sign_in_spec.rb": 17.396392849999998, + "spec/features/new_device_tracking_spec.rb": 10.381174502, + "spec/features/openid_connect/authorization_confirmation_spec.rb": 20.179389722, + "spec/features/openid_connect/openid_connect_spec.rb": 167.138706858, + "spec/features/openid_connect/phishing_resistant_required_spec.rb": 34.001085309, + "spec/features/openid_connect/redirect_uri_validation_spec.rb": 33.120771518, + "spec/features/phone/add_phone_spec.rb": 37.490217186, + "spec/features/phone/confirmation_spec.rb": 112.67856858100001, + "spec/features/phone/default_phone_selection_spec.rb": 18.725192631, + "spec/features/phone/edit_phone_spec.rb": 9.537593768, + "spec/features/phone/rate_limitting_spec.rb": 54.655647711, + "spec/features/phone/remove_phone_spec.rb": 6.6254866240000005, + "spec/features/remember_device/cookie_expiration_spec.rb": 7.325559522, + "spec/features/remember_device/phone_spec.rb": 40.628021829, + "spec/features/remember_device/revocation_spec.rb": 10.485624767000001, + "spec/features/remember_device/session_expiration_spec.rb": 3.982436988, + "spec/features/remember_device/sp_expiration_spec.rb": 211.766204891, + "spec/features/remember_device/totp_spec.rb": 51.047722848, + "spec/features/remember_device/user_opted_preference_spec.rb": 27.768803937999998, + "spec/features/remember_device/webauthn_spec.rb": 98.560121063, + "spec/features/reports/authorization_count_spec.rb": 71.401482268, + "spec/features/reports/monthly_gpo_letter_requests_report_spec.rb": 12.183763893, + "spec/features/reports/sp_active_users_report_spec.rb": 7.286086294, + "spec/features/saml/authorization_confirmation_spec.rb": 26.150682793, + "spec/features/saml/ial1/account_creation_spec.rb": 10.580017755, + "spec/features/saml/ial1_sso_spec.rb": 51.6018368, + "spec/features/saml/ial2_sso_spec.rb": 27.535485615, + "spec/features/saml/multiple_endpoints_spec.rb": 25.400298174, + "spec/features/saml/phishing_resistant_required_spec.rb": 23.513348885, + "spec/features/saml/redirect_uri_validation_spec.rb": 3.942279306, + "spec/features/saml/saml_logout_spec.rb": 24.157208545, + "spec/features/saml/saml_relay_state_spec.rb": 15.093978083, + "spec/features/saml/saml_spec.rb": 110.60438054, + "spec/features/session/decryption_spec.rb": 3.256941234, + "spec/features/session/timeout_spec.rb": 9.471305588, + "spec/features/sign_in/banned_users_spec.rb": 12.129006181, + "spec/features/sign_in/remember_device_default_spec.rb": 10.069318299, + "spec/features/sign_in/sp_return_log_spec.rb": 3.733718778, + "spec/features/sign_in/two_factor_options_spec.rb": 47.745540653, + "spec/features/sp_cost_tracking_spec.rb": 50.783324199, + "spec/features/two_factor_authentication/backup_code_sign_up_spec.rb": 24.584182124, + "spec/features/two_factor_authentication/change_factor_spec.rb": 17.24585893, + "spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb": 16.010188449, + "spec/features/two_factor_authentication/multiple_tabs_spec.rb": 11.373113592, + "spec/features/two_factor_authentication/sign_in_spec.rb": 121.83629427, + "spec/features/two_factor_authentication/sign_in_via_personal_key_spec.rb": 7.321116241, + "spec/features/users/password_recovery_via_recovery_code_spec.rb": 46.964656765, + "spec/features/users/password_reset_with_pending_profile_spec.rb": 7.237078065, + "spec/features/users/piv_cac_management_spec.rb": 30.668030266, + "spec/features/users/regenerate_personal_key_spec.rb": 8.987526887000001, + "spec/features/users/sign_in_irs_spec.rb": 30.884822841, + "spec/features/users/sign_in_spec.rb": 422.649588344, + "spec/features/users/sign_out_spec.rb": 3.43708553, + "spec/features/users/sign_up_spec.rb": 128.386760477, + "spec/features/users/totp_management_spec.rb": 19.133432851000002, + "spec/features/users/user_edit_spec.rb": 3.314308614, + "spec/features/users/user_profile_spec.rb": 41.618690916, + "spec/features/users/verify_profile_spec.rb": 14.688483096, + "spec/features/visitors/bad_password_spec.rb": 3.622186752, + "spec/features/visitors/email_confirmation_spec.rb": 22.196911488, + "spec/features/visitors/i18n_spec.rb": 38.847907676, + "spec/features/visitors/js_disabled_spec.rb": 6.339618132, + "spec/features/visitors/navigation_spec.rb": 4.181874004, + "spec/features/visitors/password_recovery_spec.rb": 66.583101644, + "spec/features/visitors/resend_email_confirmation_spec.rb": 17.253549811, + "spec/features/visitors/set_password_spec.rb": 30.015697064, + "spec/features/visitors/sign_up_with_email_spec.rb": 31.534222514, + "spec/features/webauthn/hidden_spec.rb": 24.227415872999998, + "spec/features/webauthn/management_spec.rb": 53.624379184, + "spec/features/webauthn/sign_in_spec.rb": 10.879400402, + "spec/features/webauthn/sign_up_spec.rb": 14.691343386, + "spec/forms/add_user_email_form_spec.rb": 0.5456360140000001, + "spec/forms/api/verify/document_capture_errors_delete_form_spec.rb": 0.031662077999999996, + "spec/forms/delete_user_email_form_spec.rb": 0.302865236, + "spec/forms/edit_phone_form_spec.rb": 0.227426358, + "spec/forms/event_disavowal/password_reset_from_disavowal_form_spec.rb": 0.817841122, + "spec/forms/gpo_verify_form_spec.rb": 1.131057832, + "spec/forms/idv/api_document_verification_form_spec.rb": 0.178082758, + "spec/forms/idv/api_document_verification_status_form_spec.rb": 0.142448794, + "spec/forms/idv/api_image_upload_form_spec.rb": 1.139435361, + "spec/forms/idv/doc_pii_form_spec.rb": 0.059271565, + "spec/forms/idv/phone_confirmation_otp_verification_form_spec.rb": 0.183517877, + "spec/forms/idv/phone_form_spec.rb": 0.95947762, + "spec/forms/idv/ssn_form_spec.rb": 0.154520826, + "spec/forms/idv/ssn_format_form_spec.rb": 0.072979816, + "spec/forms/new_phone_form_spec.rb": 2.031037458, + "spec/forms/openid_connect_authorize_form_spec.rb": 0.596742651, + "spec/forms/openid_connect_logout_form_spec.rb": 0.714093112, + "spec/forms/openid_connect_token_form_spec.rb": 1.913807907, + "spec/forms/otp_delivery_selection_form_spec.rb": 0.127123852, + "spec/forms/otp_verification_form_spec.rb": 0.199160617, + "spec/forms/password_form_spec.rb": 1.188525368, + "spec/forms/password_reset_email_form_spec.rb": 0.076759054, + "spec/forms/personal_key_form_spec.rb": 0.06633077400000001, + "spec/forms/register_user_email_form_spec.rb": 3.041947124, + "spec/forms/reset_password_form_spec.rb": 1.054730842, + "spec/forms/security_event_form_spec.rb": 2.542158474, + "spec/forms/totp_setup_form_spec.rb": 0.156456089, + "spec/forms/totp_verification_form_spec.rb": 0.058561933999999996, + "spec/forms/two_factor_authentication/phone_deletion_form_spec.rb": 0.449122586, + "spec/forms/two_factor_login_options_form_spec.rb": 0.09582588, + "spec/forms/two_factor_options_form_spec.rb": 0.243800354, + "spec/forms/update_email_language_form_spec.rb": 0.083828612, + "spec/forms/update_user_password_form_spec.rb": 0.926201858, + "spec/forms/user_piv_cac_login_form_spec.rb": 0.047058057, + "spec/forms/user_piv_cac_setup_form_spec.rb": 0.185118188, + "spec/forms/user_piv_cac_verification_form_spec.rb": 0.135310388, + "spec/forms/verify_password_form_spec.rb": 0.12722292500000001, + "spec/forms/verify_personal_key_form_spec.rb": 0.239359002, + "spec/forms/webauthn_setup_form_spec.rb": 0.138091492, + "spec/forms/webauthn_verification_form_spec.rb": 0.226542362, + "spec/forms/webauthn_visit_form_spec.rb": 0.141763644, + "spec/helpers/application_helper_spec.rb": 0.021086988, + "spec/helpers/asset_helper_spec.rb": 0.01549042, + "spec/helpers/aws_s3_helper_spec.rb": 0.091065602, + "spec/helpers/go_back_helper_spec.rb": 0.055581522, + "spec/helpers/link_helper_spec.rb": 0.089029812, + "spec/helpers/locale_helper_spec.rb": 0.087239343, + "spec/helpers/script_helper_spec.rb": 0.085423074, + "spec/helpers/session_timeout_warning_helper_spec.rb": 0.060790384, + "spec/i18n_spec.rb": 50.702802539, + "spec/jobs/address_proofing_job_spec.rb": 0.192242415, + "spec/jobs/application_job_spec.rb": 0.007564081, + "spec/jobs/document_proofing_job_spec.rb": 0.559700526, + "spec/jobs/fraud_rejection_daily_job_spec.rb": 0.079938747, + "spec/jobs/get_usps_proofing_results_job_spec.rb": 7.843321775, + "spec/jobs/gpo_daily_job_spec.rb": 0.104095852, + "spec/jobs/heartbeat_job_spec.rb": 0.014237247, + "spec/jobs/in_person/email_reminder_job_spec.rb": 0.877686566, + "spec/jobs/irs_attempts_events_batch_job_spec.rb": 3.639189138, + "spec/jobs/job_helpers/encryption_helper_spec.rb": 0.01002906, + "spec/jobs/job_helpers/s3_helper_spec.rb": 0.167022335, + "spec/jobs/job_helpers/stale_job_helper_spec.rb": 0.02858227, + "spec/jobs/job_helpers/timer_spec.rb": 0.0214543, + "spec/jobs/phone_number_opt_out_sync_job_spec.rb": 0.047666838, + "spec/jobs/reports/agreement_summary_report_spec.rb": 0.123715904, + "spec/jobs/reports/base_report_spec.rb": 0.037328095, + "spec/jobs/reports/combined_invoice_supplement_report_spec.rb": 0.324706043, + "spec/jobs/reports/daily_auths_report_spec.rb": 0.166715463, + "spec/jobs/reports/daily_dropoffs_report_spec.rb": 0.097023199, + "spec/jobs/reports/daily_registration_report_spec.rb": 0.138372927, + "spec/jobs/reports/deleted_user_accounts_report_spec.rb": 0.090197228, + "spec/jobs/reports/duplicate_ssn_report_spec.rb": 0.10172186400000001, + "spec/jobs/reports/irs_weekly_summary_report_spec.rb": 0.285212858, + "spec/jobs/reports/month_helper_spec.rb": 0.014984619000000001, + "spec/jobs/reports/query_helpers_spec.rb": 0.019143998, + "spec/jobs/reports/sp_active_users_report_spec.rb": 0.071096301, + "spec/jobs/reports/sp_user_counts_report_spec.rb": 0.053266291, + "spec/jobs/reports/total_ial2_costs_report_spec.rb": 0.034394596, + "spec/jobs/reports/total_monthly_auths_report_spec.rb": 0.042859366999999995, + "spec/jobs/reports/verification_failures_report_spec.rb": 0.477205394, + "spec/jobs/resolution_proofing_job_spec.rb": 0.588646698, + "spec/jobs/risc_delivery_job_spec.rb": 0.169518108, + "spec/jobs/threat_metrix_js_verification_job_spec.rb": 0.981613087, + "spec/lib/ab_test_bucket_spec.rb": 0.061471894, + "spec/lib/analytics_events_documenter_spec.rb": 0.190323129, + "spec/lib/app_artifacts_spec.rb": 0.048736892, + "spec/lib/asset_sources_spec.rb": 0.112079159, + "spec/lib/aws/ses_spec.rb": 0.037517965, + "spec/lib/base16_spec.rb": 0.026916704, + "spec/lib/data_requests/deployed/create_email_addresses_report_spec.rb": 0.02253286, + "spec/lib/data_requests/deployed/create_mfa_configurations_report_spec.rb": 0.117324761, + "spec/lib/data_requests/deployed/create_user_events_report_spec.rb": 0.048524675, + "spec/lib/data_requests/deployed/create_user_report_spec.rb": 0.09963783600000001, + "spec/lib/data_requests/deployed/lookup_shared_device_users_spec.rb": 0.050149732, + "spec/lib/data_requests/deployed/lookup_user_by_uuid_spec.rb": 0.057556582, + "spec/lib/data_requests/local/fetch_cloudwatch_logs_spec.rb": 0.037973312, + "spec/lib/data_requests/local/write_cloudwatch_logs_spec.rb": 0.028919119, + "spec/lib/data_requests/local/write_user_events_spec.rb": 0.009915095, + "spec/lib/data_requests/local/write_user_info_spec.rb": 0.009470691, + "spec/lib/deploy/activate_spec.rb": 0.13826028099999998, + "spec/lib/feature_management_spec.rb": 0.397662774, + "spec/lib/fingerprinter_spec.rb": 0.015419853, + "spec/lib/good_job_connection_pool_size_spec.rb": 0.036179908999999996, + "spec/lib/headers_filter_spec.rb": 0.008766337, + "spec/lib/identity_config_spec.rb": 0.025962357, + "spec/lib/identity_cors_spec.rb": 0.024493265, + "spec/lib/identity_job_log_subscriber_spec.rb": 0.164736772, + "spec/lib/linters/errors_add_linter_spec.rb": 0.04352469, + "spec/lib/linters/image_size_linter_spec.rb": 0.085625468, + "spec/lib/linters/localized_validation_message_linter_spec.rb": 0.092063957, + "spec/lib/linters/mail_later_linter_spec.rb": 0.077226566, + "spec/lib/linters/redirect_back_linter_spec.rb": 0.076755621, + "spec/lib/linters/url_options_linter_spec.rb": 0.095077736, + "spec/lib/makefile_help_parser_spec.rb": 0.080842259, + "spec/lib/otp_code_generator_spec.rb": 0.03034179, + "spec/lib/pinpoint_supported_countries_spec.rb": 0.119058425, + "spec/lib/query_tracker_spec.rb": 0.035661485, + "spec/lib/reporting/authentication_report_spec.rb": 0.010312284, + "spec/lib/reporting/cloudwatch_client_spec.rb": 1.020889211, + "spec/lib/reporting/cloudwatch_query_quoting_spec.rb": 0.016911478, + "spec/lib/reporting/command_line_options_spec.rb": 0.051185253, + "spec/lib/reporting/identity_verification_report_spec.rb": 0.014863927999999998, + "spec/lib/session_encryptor_spec.rb": 0.090182567, + "spec/lib/tasks/dev_rake_spec.rb": 12.508507087, + "spec/lib/tasks/partners_rake_spec.rb": 0.902318065, + "spec/lib/tasks/review_profile_spec.rb": 1.1079302340000001, + "spec/lib/tasks/rotate_rake_spec.rb": 0.30446941899999996, + "spec/lib/telephony/alert_sender_spec.rb": 0.067202754, + "spec/lib/telephony/otp_sender_spec.rb": 0.209770442, + "spec/lib/telephony/pinpoint/aws_credential_builder_spec.rb": 0.028222088, + "spec/lib/telephony/pinpoint/opt_out_manager_spec.rb": 0.10640153299999999, + "spec/lib/telephony/pinpoint/sms_sender_spec.rb": 0.211558586, + "spec/lib/telephony/pinpoint/voice_sender_spec.rb": 0.097395808, + "spec/lib/telephony/response_spec.rb": 0.042006122, + "spec/lib/telephony/telephony_spec.rb": 0.078356267, + "spec/lib/telephony/test/call_spec.rb": 0.068570091, + "spec/lib/telephony/test/message_spec.rb": 0.043978130000000004, + "spec/lib/telephony/test/sms_sender_spec.rb": 0.045974666, + "spec/lib/telephony/test/voice_sender_spec.rb": 0.022395638000000002, + "spec/lib/telephony/util_spec.rb": 0.007113312, + "spec/lib/utf8_sanitizer_spec.rb": 0.059890347000000003, + "spec/mailers/previews/user_mailer_preview_spec.rb": 0.292095632, + "spec/mailers/report_mailer_spec.rb": 0.10244007399999999, + "spec/mailers/user_mailer_spec.rb": 3.676390663, + "spec/models/account_reset_request_spec.rb": 0.057218896, + "spec/models/agency_identity_spec.rb": 0.030335927999999998, + "spec/models/agency_spec.rb": 0.046644419, + "spec/models/agreements/iaa_gtc_spec.rb": 0.306059918, + "spec/models/agreements/iaa_order_spec.rb": 0.470808725, + "spec/models/agreements/iaa_spec.rb": 0.245824153, + "spec/models/agreements/integration_spec.rb": 0.34108569, + "spec/models/agreements/integration_status_spec.rb": 0.06724306000000001, + "spec/models/agreements/integration_usage_spec.rb": 0.26668758, + "spec/models/agreements/partner_account_spec.rb": 0.170270273, + "spec/models/agreements/partner_account_status_spec.rb": 0.083461415, + "spec/models/anonymous_user_spec.rb": 0.039631976, + "spec/models/backup_code_configuration_spec.rb": 1.294288719, + "spec/models/concerns/user_otp_methods_spec.rb": 0.00950226, + "spec/models/deleted_user_spec.rb": 0.079463014, + "spec/models/device_spec.rb": 0.086995835, + "spec/models/document_capture_session_spec.rb": 0.072794356, + "spec/models/email_address_spec.rb": 0.207938978, + "spec/models/event_spec.rb": 0.041311343, + "spec/models/gpo_confirmation_code_spec.rb": 0.127433969, + "spec/models/in_person_enrollment_spec.rb": 1.028460284, + "spec/models/null_identity_spec.rb": 0.007449214, + "spec/models/phone_configuration_spec.rb": 0.134042061, + "spec/models/phone_number_opt_out_spec.rb": 0.132228465, + "spec/models/profile_spec.rb": 1.922392905, + "spec/models/service_provider_identity_spec.rb": 0.443261182, + "spec/models/service_provider_spec.rb": 0.097290375, + "spec/models/sp_return_log_spec.rb": 0.009612778, + "spec/models/user_spec.rb": 2.071524169, + "spec/models/webauthn_configuration_spec.rb": 0.160580897, + "spec/policies/backup_code_policy_spec.rb": 0.047170202, + "spec/policies/service_provider_mfa_policy_spec.rb": 1.056282868, + "spec/policies/two_factor_authentication/piv_cac_policy_spec.rb": 0.070412291, + "spec/policies/user_mfa_policy_spec.rb": 0.199092249, + "spec/policies/webauthn_login_option_policy_spec.rb": 0.07133541, + "spec/presenters/account_reset/pending_presenter_spec.rb": 0.143367138, + "spec/presenters/account_show_presenter_spec.rb": 0.177017309, + "spec/presenters/cancellation_presenter_spec.rb": 0.030601714, + "spec/presenters/completions_presenter_spec.rb": 0.563997295, + "spec/presenters/confirm_delete_email_presenter_spec.rb": 0.021897057, + "spec/presenters/eastern_time_presenter_spec.rb": 0.007312063, + "spec/presenters/idv/cancellations_presenter_spec.rb": 0.042703547, + "spec/presenters/idv/gpo_presenter_spec.rb": 0.27275645, + "spec/presenters/idv/in_person/ready_to_verify_presenter_spec.rb": 0.378023641, + "spec/presenters/idv/in_person/verification_results_email_presenter_spec.rb": 0.491749575, + "spec/presenters/image_upload_response_presenter_spec.rb": 0.111157361, + "spec/presenters/max_attempts_reached_presenter_spec.rb": 0.02859288, + "spec/presenters/mfa_confirmation_presenter_spec.rb": 0.055629244, + "spec/presenters/navigation_presenter_spec.rb": 0.128845831, + "spec/presenters/openid_connect_certs_presenter_spec.rb": 0.00879459, + "spec/presenters/openid_connect_configuration_presenter_spec.rb": 0.00937257, + "spec/presenters/openid_connect_user_info_presenter_spec.rb": 0.50639289, + "spec/presenters/piv_cac_authentication_setup_presenter_spec.rb": 0.052806555, + "spec/presenters/piv_cac_error_presenter_spec.rb": 0.029971705, + "spec/presenters/risc_configuration_presenter_spec.rb": 0.007505527, + "spec/presenters/saml_request_presenter_spec.rb": 0.038690222, + "spec/presenters/session_timeout_modal_presenter_spec.rb": 0.014778803, + "spec/presenters/setup_presenter_spec.rb": 0.079369594, + "spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb": 0.028849325, + "spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb": 0.032277671, + "spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb": 0.007990885, + "spec/presenters/two_factor_auth_code/personal_key_presenter_spec.rb": 0.014080891, + "spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb": 0.070801363, + "spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb": 0.122388517, + "spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb": 0.149058269, + "spec/presenters/two_factor_authentication/auth_app_selection_presenter_spec.rb": 0.057092587, + "spec/presenters/two_factor_authentication/personal_key_selection_presenter_spec.rb": 0.006362566, + "spec/presenters/two_factor_authentication/phone_selection_presenter_spec.rb": 0.147669572, + "spec/presenters/two_factor_authentication/piv_cac_selection_presenter_spec.rb": 0.066833743, + "spec/presenters/two_factor_authentication/selection_presenter_spec.rb": 0.035486058, + "spec/presenters/two_factor_authentication/sms_selection_presenter_spec.rb": 0.078822224, + "spec/presenters/two_factor_authentication/voice_selection_presenter_spec.rb": 0.105602242, + "spec/presenters/two_factor_authentication/webauthn_platform_selection_presenter_spec.rb": 0.104484259, + "spec/presenters/two_factor_authentication/webauthn_selection_presenter_spec.rb": 0.078740212, + "spec/presenters/two_factor_login_options_presenter_spec.rb": 0.175141757, + "spec/presenters/two_factor_options_presenter_spec.rb": 0.051090845999999995, + "spec/presenters/utc_time_presenter_spec.rb": 0.007486452, + "spec/presenters/webauthn_setup_presenter_spec.rb": 0.222969207, + "spec/requests/acuant_sdk_spec.rb": 0.09939785899999999, + "spec/requests/api_cors_spec.rb": 0.513065068, + "spec/requests/csp_spec.rb": 0.14527948499999999, + "spec/requests/headers_spec.rb": 0.246363634, + "spec/requests/i18n_spec.rb": 0.120136023, + "spec/requests/invalid_encoding_spec.rb": 0.295391124, + "spec/requests/invalid_sign_in_params_spec.rb": 0.09459721900000001, + "spec/requests/not_acceptable_spec.rb": 0.094575783, + "spec/requests/openid_connect_authorize_spec.rb": 0.751280258, + "spec/requests/openid_connect_cors_spec.rb": 0.538410748, + "spec/requests/page_not_found_spec.rb": 0.130961063, + "spec/requests/rack_attack_spec.rb": 6.875212267999999, + "spec/requests/redirects_spec.rb": 0.17369221699999998, + "spec/requests/redis_down_spec.rb": 0.037052368, + "spec/requests/saml_requests_spec.rb": 0.088256373, + "spec/requests/secure_cookies_spec.rb": 0.10017601500000001, + "spec/routing/gpo_verification_routing_spec.rb": 0.279416249, + "spec/scripts/changelog_check_spec.rb": 0.091737529, + "spec/services/access_token_verifier_spec.rb": 0.07655946500000001, + "spec/services/account_reset/cancel_request_for_user_spec.rb": 0.463988287, + "spec/services/account_reset/cancel_spec.rb": 1.169718978, + "spec/services/account_reset/create_request_spec.rb": 0.684636089, + "spec/services/account_reset/delete_account_spec.rb": 0.606466707, + "spec/services/account_reset/find_prending_request_for_user_spec.rb": 0.115164372, + "spec/services/account_reset/grant_request_spec.rb": 0.031229465, + "spec/services/account_reset/grant_requests_and_send_emails_spec.rb": 1.328006576, + "spec/services/account_reset/notify_user_of_request_cancellation_spec.rb": 0.62713688, + "spec/services/account_reset/validate_granted_token_spec.rb": 0.040424776, + "spec/services/active_profile_encryptor_spec.rb": 0.05254814, + "spec/services/agency_identity_linker_spec.rb": 0.40137527, + "spec/services/agency_seeder_spec.rb": 0.062155219, + "spec/services/agreements/iaa_gtc_seeder_spec.rb": 0.045117687000000004, + "spec/services/agreements/iaa_order_seeder_spec.rb": 0.077702734, + "spec/services/agreements/integration_seeder_spec.rb": 0.066703186, + "spec/services/agreements/integration_status_seeder_spec.rb": 0.035570467, + "spec/services/agreements/partner_account_seeder_spec.rb": 0.043904704, + "spec/services/agreements/partner_account_status_seeder_spec.rb": 0.051665759000000006, + "spec/services/analytics_spec.rb": 0.234241117, + "spec/services/arcgis_api/geocoder_spec.rb": 0.41724231700000003, + "spec/services/attribute_asserter_spec.rb": 1.546701598, + "spec/services/backup_code_generator_spec.rb": 1.479039338, + "spec/services/banned_user_resolver_spec.rb": 0.136806634, + "spec/services/barcode_outputter_spec.rb": 0.018576909, + "spec/services/browser_cache_spec.rb": 0.021734859, + "spec/services/browser_support_spec.rb": 0.093884178, + "spec/services/calendar_service_spec.rb": 0.122989104, + "spec/services/cloud_front_header_parser_spec.rb": 0.0319161, + "spec/services/completions_decider_spec.rb": 0.051105304000000004, + "spec/services/database_health_checker_spec.rb": 0.021446327, + "spec/services/date_parser_spec.rb": 0.037658146, + "spec/services/db/add_document_verification_and_selfie_costs_spec.rb": 0.042534051, + "spec/services/db/identity/sp_active_user_counts_spec.rb": 0.07088024, + "spec/services/db/identity/sp_user_counts_spec.rb": 0.029031567, + "spec/services/db/monthly_auth_count/total_monthly_auth_counts_spec.rb": 0.045848539, + "spec/services/db/monthly_sp_auth_count/total_monthly_auth_counts_within_iaa_window_spec.rb": 0.11222610699999999, + "spec/services/db/monthly_sp_auth_count/unique_monthly_auth_counts_by_iaa_spec.rb": 0.160131191, + "spec/services/db/sp_return_log_spec.rb": 0.013286332, + "spec/services/deleted_accounts_report_spec.rb": 0.157554418, + "spec/services/displayable_pii_formatter_spec.rb": 0.879316827, + "spec/services/doc_auth/acuant/acuant_client_spec.rb": 0.301843504, + "spec/services/doc_auth/acuant/pii_from_doc_spec.rb": 0.030922839, + "spec/services/doc_auth/acuant/request_spec.rb": 0.558747325, + "spec/services/doc_auth/acuant/requests/create_document_request_spec.rb": 0.074538876, + "spec/services/doc_auth/acuant/requests/get_results_request_spec.rb": 0.07982211, + "spec/services/doc_auth/acuant/requests/upload_image_request_spec.rb": 0.072700706, + "spec/services/doc_auth/acuant/responses/create_document_response_spec.rb": 0.009226863, + "spec/services/doc_auth/acuant/responses/get_results_response_spec.rb": 0.126992819, + "spec/services/doc_auth/acuant/result_codes_spec.rb": 0.015058334, + "spec/services/doc_auth/error_generator_spec.rb": 0.169202135, + "spec/services/doc_auth/lexis_nexis/lexis_nexis_client_spec.rb": 0.330498073, + "spec/services/doc_auth/lexis_nexis/request_spec.rb": 0.547362534, + "spec/services/doc_auth/lexis_nexis/requests/true_id_request_spec.rb": 0.105806142, + "spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb": 0.503873076, + "spec/services/doc_auth/mock/dock_auth_mock_client_spec.rb": 0.091080398, + "spec/services/doc_auth/mock/result_response_spec.rb": 0.117753533, + "spec/services/doc_auth/processed_alert_to_log_alert_formatter_spec.rb": 0.018258746, + "spec/services/doc_auth/response_spec.rb": 0.128130091, + "spec/services/doc_auth_router_spec.rb": 0.095799855, + "spec/services/document_capture_session_async_result_spec.rb": 0.049821769, + "spec/services/document_capture_session_result_spec.rb": 0.008328064, + "spec/services/duration_parser_spec.rb": 0.072267957, + "spec/services/email_confirmation_token_validator_spec.rb": 0.161345248, + "spec/services/encrypted_attribute_spec.rb": 0.039945239, + "spec/services/encrypted_document_storage/document_writer_spec.rb": 0.05515934, + "spec/services/encrypted_document_storage/local_storage_spec.rb": 0.006122147, + "spec/services/encrypted_document_storage/s3_storage_spec.rb": 0.011976658, + "spec/services/encrypted_redis_struct_storage_spec.rb": 0.112913095, + "spec/services/encryption/aes_cipher_spec.rb": 0.030430366, + "spec/services/encryption/aes_cipher_v2_spec.rb": 0.023787243, + "spec/services/encryption/contextless_kms_client_spec.rb": 0.148588384, + "spec/services/encryption/encryptors/aes_encryptor_spec.rb": 0.024738347, + "spec/services/encryption/encryptors/aes_encryptor_v2_spec.rb": 0.023045868, + "spec/services/encryption/encryptors/attribute_encryptor_spec.rb": 0.047064122, + "spec/services/encryption/encryptors/background_proofing_arg_encryptor_spec.rb": 0.025864836999999998, + "spec/services/encryption/encryptors/pii_encryptor_spec.rb": 0.092263229, + "spec/services/encryption/encryptors/session_encryptor_spec.rb": 0.023114612, + "spec/services/encryption/kms_client_spec.rb": 0.1873834, + "spec/services/encryption/kms_logger_spec.rb": 0.022014373, + "spec/services/encryption/multi_region_kms_client_spec.rb": 0.081048674, + "spec/services/encryption/password_verifier_spec.rb": 0.10723056, + "spec/services/encryption/uak_password_verifier_spec.rb": 0.250484399, + "spec/services/encryption/user_access_key_spec.rb": 0.084086562, + "spec/services/event_disavowal/disavow_event_spec.rb": 0.025417099, + "spec/services/event_disavowal/find_disavowed_event_spec.rb": 0.062397093, + "spec/services/event_disavowal/validate_disavowed_event_spec.rb": 0.08787130900000001, + "spec/services/forget_all_browsers_spec.rb": 0.020908084, + "spec/services/form_response_spec.rb": 0.194205025, + "spec/services/frontend_logger_spec.rb": 0.018131561, + "spec/services/funnel/registration/add_mfa_spec.rb": 0.038040219, + "spec/services/funnel/registration/total_registered_count_spec.rb": 0.110147248, + "spec/services/gpo_confirmation_exporter_spec.rb": 0.023577765, + "spec/services/gpo_confirmation_maker_spec.rb": 0.180253283, + "spec/services/gpo_confirmation_spec.rb": 0.023930607, + "spec/services/gpo_confirmation_uploader_spec.rb": 0.069140379, + "spec/services/gpo_daily_test_sender_spec.rb": 0.056535775999999996, + "spec/services/health_check_summary_spec.rb": 0.0151433, + "spec/services/iaa_reporting_helper_spec.rb": 0.183439879, + "spec/services/ial_context_spec.rb": 0.464090626, + "spec/services/id_token_builder_spec.rb": 0.334396084, + "spec/services/identity_linker_spec.rb": 0.288367367, + "spec/services/idv/actions/verify_document_status_action_spec.rb": 0.272389584, + "spec/services/idv/agent_spec.rb": 0.419524769, + "spec/services/idv/analytics_events_enhancer_spec.rb": 0.053621465, + "spec/services/idv/cancel_verification_attempt_spec.rb": 0.219316008, + "spec/services/idv/data_url_image_spec.rb": 0.02997306, + "spec/services/idv/doc_auth_form_response_spec.rb": 0.053093869, + "spec/services/idv/duplicate_ssn_finder_spec.rb": 0.222775772, + "spec/services/idv/gpo_mail_spec.rb": 0.124873332, + "spec/services/idv/in_person/completion_survey_sender_spec.rb": 0.674142688, + "spec/services/idv/in_person/enrollment_code_formatter_spec.rb": 0.008019318, + "spec/services/idv/in_person_config_spec.rb": 0.107334034, + "spec/services/idv/phone_confirmation_session_spec.rb": 0.06028332, + "spec/services/idv/phone_step_spec.rb": 0.830866348, + "spec/services/idv/profile_maker_spec.rb": 0.22977045200000001, + "spec/services/idv/proofing_components_logging_spec.rb": 0.021217593, + "spec/services/idv/send_phone_confirmation_otp_spec.rb": 0.191641493, + "spec/services/idv/session_spec.rb": 0.915339425, + "spec/services/idv/steps/document_capture_step_spec.rb": 0.182655802, + "spec/services/idv/steps/in_person/address_step_spec.rb": 0.217891119, + "spec/services/idv/steps/in_person/ssn_step_spec.rb": 0.081506788, + "spec/services/idv/steps/in_person/state_id_step_spec.rb": 0.189685245, + "spec/services/idv/steps/in_person/verify_step_spec.rb": 0.27147140399999997, + "spec/services/idv/steps/in_person/verify_wait_step_show_spec.rb": 0.280391025, + "spec/services/idv/steps/upload_step_spec.rb": 0.052712836, + "spec/services/idv/steps/welcome_step_spec.rb": 0.06748736899999999, + "spec/services/image_upload_presigned_url_generator_spec.rb": 0.024556504, + "spec/services/irs_attempts_api/attempt_event_spec.rb": 1.251784171, + "spec/services/irs_attempts_api/envelope_encryptor_spec.rb": 3.348876783, + "spec/services/irs_attempts_api/redis_client_spec.rb": 0.030896200999999998, + "spec/services/irs_attempts_api/tracker_spec.rb": 0.442496033, + "spec/services/key_rotator/attribute_encryption_spec.rb": 0.076784434, + "spec/services/key_rotator/hmac_fingerprinter_spec.rb": 0.144765468, + "spec/services/maintenance_window_spec.rb": 0.045502836, + "spec/services/marketing_site_spec.rb": 0.242773315, + "spec/services/multi_health_checker_spec.rb": 0.020096313, + "spec/services/openid_connect_attribute_scoper_spec.rb": 0.093507757, + "spec/services/otp_preference_updater_spec.rb": 0.05561098, + "spec/services/otp_rate_limiter_spec.rb": 0.146888721, + "spec/services/out_of_band_session_accessor_spec.rb": 0.035128526, + "spec/services/outage_status_spec.rb": 0.212980414, + "spec/services/outbound_health_checker_spec.rb": 0.1255733, + "spec/services/parse_controller_from_referer_spec.rb": 0.016238258999999998, + "spec/services/personal_key_generator_spec.rb": 0.358131293, + "spec/services/phone_formatter_spec.rb": 0.072818469, + "spec/services/phone_number_capabilities_spec.rb": 0.236355699, + "spec/services/phone_recaptcha_validator_spec.rb": 0.11327219100000001, + "spec/services/pii/attributes_spec.rb": 0.052501571, + "spec/services/pii/cacher_spec.rb": 0.47407567899999997, + "spec/services/pii/fingerprinter_spec.rb": 0.074099072, + "spec/services/pii/re_encryptor_spec.rb": 0.120902764, + "spec/services/piv_cac/check_config_spec.rb": 0.019717316999999998, + "spec/services/piv_cac_service_spec.rb": 0.15460014, + "spec/services/profanity_detector_spec.rb": 0.052402745, + "spec/services/proofing/aamva/applicant_spec.rb": 0.02306904, + "spec/services/proofing/aamva/authentication_client_spec.rb": 0.20816283100000002, + "spec/services/proofing/aamva/hmac_secret_spec.rb": 0.008360152, + "spec/services/proofing/aamva/proofing_spec.rb": 0.24020803100000002, + "spec/services/proofing/aamva/request/authentication_token_request_spec.rb": 0.248799952, + "spec/services/proofing/aamva/request/security_token_request_spec.rb": 1.794116499, + "spec/services/proofing/aamva/request/verification_request_spec.rb": 0.279082606, + "spec/services/proofing/aamva/response/authentication_token_response_spec.rb": 0.057684673, + "spec/services/proofing/aamva/response/security_token_response_spec.rb": 0.095749796, + "spec/services/proofing/aamva/response/verification_response_spec.rb": 0.32805449200000003, + "spec/services/proofing/aamva/soap_error_handler_spec.rb": 0.082296091, + "spec/services/proofing/aamva/verification_client_spec.rb": 0.489217931, + "spec/services/proofing/ddp_result_spec.rb": 0.126256307, + "spec/services/proofing/lexis_nexis/date_formatter_spec.rb": 0.023691009, + "spec/services/proofing/lexis_nexis/ddp/proofing_spec.rb": 0.066945244, + "spec/services/proofing/lexis_nexis/ddp/response_redacter_spec.rb": 0.025849511999999998, + "spec/services/proofing/lexis_nexis/ddp/verification_request_spec.rb": 0.025268374, + "spec/services/proofing/lexis_nexis/instant_verify/check_to_attribute_mapper_spec.rb": 0.052876516, + "spec/services/proofing/lexis_nexis/instant_verify/proofing_spec.rb": 0.195741817, + "spec/services/proofing/lexis_nexis/instant_verify/verification_request_spec.rb": 0.068235424, + "spec/services/proofing/lexis_nexis/phone_finder/proofing_spec.rb": 0.122634362, + "spec/services/proofing/lexis_nexis/phone_finder/verification_request_spec.rb": 0.077633495, + "spec/services/proofing/lexis_nexis/response_spec.rb": 0.063842998, + "spec/services/proofing/lexis_nexis/verification_error_parser_spec.rb": 0.035667648999999996, + "spec/services/proofing/mock/address_mock_client_spec.rb": 0.025629891000000002, + "spec/services/proofing/mock/ddp_mock_client_spec.rb": 0.044225425, + "spec/services/proofing/mock/device_profiling_backend_spec.rb": 0.016206738000000002, + "spec/services/proofing/mock/resolution_mock_client_spec.rb": 0.055217221, + "spec/services/proofing/mock/state_id_mock_client_spec.rb": 0.037586472, + "spec/services/proofing/resolution_result_adjudicator_spec.rb": 0.033485077, + "spec/services/proofing_session_async_result_spec.rb": 0.014494836, + "spec/services/push_notification/account_purged_event_spec.rb": 0.029994371, + "spec/services/push_notification/email_changed_event_spec.rb": 0.02787753, + "spec/services/push_notification/http_push_spec.rb": 0.621945155, + "spec/services/push_notification/identifier_recycled_event_spec.rb": 0.026411327999999998, + "spec/services/push_notification/mfa_limit_account_locked_event_spec.rb": 0.031767904, + "spec/services/push_notification/password_reset_event_spec.rb": 0.027084327, + "spec/services/push_notification/recovery_activated_event_spec.rb": 0.030530783, + "spec/services/push_notification/reproof_completed_event_spec.rb": 0.03078486, + "spec/services/pwned_passwords/lookup_password_spec.rb": 0.01506096, + "spec/services/random_phrase_spec.rb": 0.037123024, + "spec/services/reactivate_account_session_spec.rb": 0.140854031, + "spec/services/recaptcha_mock_validator_spec.rb": 0.038526107, + "spec/services/recaptcha_validator_spec.rb": 0.286191666, + "spec/services/redis_rate_limiter_spec.rb": 0.059155562, + "spec/services/remember_device_cookie_spec.rb": 0.176597241, + "spec/services/request_password_reset_spec.rb": 3.199050218, + "spec/services/reset_user_password_spec.rb": 1.785525947, + "spec/services/revoke_service_provider_consent_spec.rb": 0.03152246, + "spec/services/saml_endpoint_spec.rb": 0.064391223, + "spec/services/saml_request_validator_spec.rb": 0.116813682, + "spec/services/secure_headers_allow_list_spec.rb": 0.035059107, + "spec/services/send_sign_up_email_confirmation_spec.rb": 1.095458277, + "spec/services/service_provider_request_proxy_spec.rb": 0.064112945, + "spec/services/service_provider_seeder_spec.rb": 1.496502392, + "spec/services/service_provider_updater_spec.rb": 0.45303152, + "spec/services/session_encryptor_spec.rb": 0.02276282, + "spec/services/sp_return_url_resolver_spec.rb": 0.089280987, + "spec/services/ssn_formatter_spec.rb": 0.064549527, + "spec/services/store_sp_metadata_in_session_spec.rb": 0.030212393, + "spec/services/string_redacter_spec.rb": 0.007479016, + "spec/services/throttle_spec.rb": 0.150838164, + "spec/services/time_service_spec.rb": 0.008286567, + "spec/services/update_user_spec.rb": 0.243611109, + "spec/services/uri_service_spec.rb": 0.048279977, + "spec/services/user_alerts/alert_user_about_account_verified_spec.rb": 0.580092159, + "spec/services/user_alerts/alert_user_about_new_device_spec.rb": 0.416537168, + "spec/services/user_alerts/alert_user_about_password_change_spec.rb": 0.40544898, + "spec/services/user_alerts/alert_user_about_personal_key_sign_in_spec.rb": 0.442179963, + "spec/services/user_event_creator_spec.rb": 0.24851092, + "spec/services/user_seeder_spec.rb": 2.757052879, + "spec/services/user_session_context_spec.rb": 0.054993397, + "spec/services/usps_in_person_proofing/enrollment_helper_spec.rb": 3.513400313, + "spec/services/usps_in_person_proofing/proofer_spec.rb": 0.441451188, + "spec/services/usps_in_person_proofing/transliterable_validator_spec.rb": 0.132614606, + "spec/services/usps_in_person_proofing/transliterator_spec.rb": 0.339624196, + "spec/services/uuid_reporter_spec.rb": 0.231941375, + "spec/services/x509/attribute_spec.rb": 0.007239249, + "spec/services/x509/attributes_spec.rb": 0.035395995, + "spec/svg_spec.rb": 1.037545143, + "spec/views/account_reset/cancel/show.html.erb_spec.rb": 0.027495574000000002, + "spec/views/account_reset/confirm_delete_account/show.html.erb_spec.rb": 0.038532393, + "spec/views/account_reset/confirm_request/show.html.erb_spec.rb": 0.010109552, + "spec/views/account_reset/delete_account/show.html.erb_spec.rb": 0.025564153, + "spec/views/account_reset/recovery_options/show.html.erb_spec.rb": 0.027236882, + "spec/views/account_reset/request/show.html.erb_spec.rb": 0.080220089, + "spec/views/account_reset/user_mailer/email_confirmation_instructions.html.erb_spec.rb": 0.117099683, + "spec/views/account_reset/user_mailer/unconfirmed_email_instructions.html.erb_spec.rb": 0.084849485, + "spec/views/accounts/_nav_auth.html.erb_spec.rb": 0.086775397, + "spec/views/accounts/connected_accounts/show.html.erb_spec.rb": 0.079172743, + "spec/views/accounts/history/show.html.erb_spec.rb": 0.074563569, + "spec/views/accounts/show.html.erb_spec.rb": 0.907920787, + "spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb": 0.218638628, + "spec/views/devise/passwords/edit.html.erb_spec.rb": 0.145286793, + "spec/views/devise/passwords/new.html.erb_spec.rb": 0.122298059, + "spec/views/devise/sessions/new.html.erb_spec.rb": 0.311074075, + "spec/views/devise/shared/_password_strength.html.erb_spec.rb": 0.030884156, + "spec/views/forgot_password/show.html.erb_spec.rb": 0.08115992, + "spec/views/idv/activated.html.erb_spec.rb": 0.018504194, + "spec/views/idv/cancellations/destroy.html.erb_spec.rb": 0.022236137, + "spec/views/idv/cancellations/new.html.erb_spec.rb": 0.083915747, + "spec/views/idv/come_back_later/show.html.erb_spec.rb": 0.057678448, + "spec/views/idv/doc_auth/_cancel.html.erb_spec.rb": 0.029647781, + "spec/views/idv/doc_auth/welcome.html.erb_spec.rb": 0.131283137, + "spec/views/idv/gpo/index.html.erb_spec.rb": 0.167210509, + "spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb": 0.48577042800000003, + "spec/views/idv/phone/new.html.erb_spec.rb": 0.127702654, + "spec/views/idv/phone_errors/_warning.html.erb_spec.rb": 0.099779457, + "spec/views/idv/phone_errors/failure.html.erb_spec.rb": 0.188355827, + "spec/views/idv/phone_errors/jobfail.html.erb_spec.rb": 0.078352151, + "spec/views/idv/phone_errors/timeout.html.erb_spec.rb": 0.074833886, + "spec/views/idv/phone_errors/warning.html.erb_spec.rb": 0.235660953, + "spec/views/idv/please_call/show.html.erb_spec.rb": 0.026478493, + "spec/views/idv/review/new.html.erb_spec.rb": 0.257268326, + "spec/views/idv/session_errors/exception.html.erb_spec.rb": 0.043186901, + "spec/views/idv/session_errors/failure.html.erb_spec.rb": 0.030970952, + "spec/views/idv/session_errors/throttled.html.erb_spec.rb": 0.052929332, + "spec/views/idv/session_errors/warning.html.erb_spec.rb": 0.07114673, + "spec/views/idv/shared/_back.html.erb_spec.rb": 0.11076280799999999, + "spec/views/idv/shared/_document_capture.html.erb_spec.rb": 0.094721841, + "spec/views/idv/shared/_error.html.erb_spec.rb": 0.204260881, + "spec/views/idv/shared/_ssn.html.erb_spec.rb": 0.087453943, + "spec/views/idv/unavailable/show.html.erb_spec.rb": 0.076485933, + "spec/views/layouts/application.html.erb_spec.rb": 0.291218951, + "spec/views/layouts/user_mailer.html.erb_spec.rb": 0.214147414, + "spec/views/mfa_confirmation/show.html.erb_spec.rb": 0.11407014, + "spec/views/partials/multi_factor_authentication/_mfa_selection.html.erb_spec.rb": 0.096781725, + "spec/views/partials/personal_key/_key.html.erb_spec.rb": 0.052162107, + "spec/views/phone_setup/index.html.erb_spec.rb": 0.438273894, + "spec/views/phone_setup/spam_protection.html.erb_spec.rb": 0.10028938, + "spec/views/reactivate_account/index.html.erb_spec.rb": 0.045763681, + "spec/views/shared/_address.html.erb_spec.rb": 0.018385801, + "spec/views/shared/_banner.html.erb_spec.rb": 0.015734672, + "spec/views/shared/_email_languages.html.erb_spec.rb": 0.064889984, + "spec/views/shared/_footer_lite.html.erb_spec.rb": 0.053308845, + "spec/views/shared/_maintenance_window_alert.html.erb_spec.rb": 0.037222589, + "spec/views/shared/_masked_text.html.erb_spec.rb": 0.037445212, + "spec/views/shared/_nav_branded.html.erb_spec.rb": 0.038126881, + "spec/views/shared/_nav_lite.html.erb_spec.rb": 0.009702337, + "spec/views/shared/_personal_key.html.erb_spec.rb": 0.016604356, + "spec/views/shared/_troubleshooting_options.html.erb_spec.rb": 0.078339002, + "spec/views/sign_up/completions/show.html.erb_spec.rb": 0.262895097, + "spec/views/sign_up/email_resend/new.html.erb_spec.rb": 0.025757625, + "spec/views/sign_up/emails/show.html.erb_spec.rb": 0.042786726000000004, + "spec/views/sign_up/passwords/new.html.erb_spec.rb": 0.089472192, + "spec/views/sign_up/registrations/new.html.erb_spec.rb": 0.139687924, + "spec/views/two_factor_authentication/options/index.html.erb_spec.rb": 0.12746154, + "spec/views/two_factor_authentication/otp_expired/show.html.erb_spec.rb": 0.090631826, + "spec/views/two_factor_authentication/otp_verification/show.html.erb_spec.rb": 0.518435305, + "spec/views/two_factor_authentication/personal_key_verification/show.html.erb_spec.rb": 0.146216537, + "spec/views/two_factor_authentication/sms_opt_in/error.html.erb_spec.rb": 0.075920482, + "spec/views/two_factor_authentication/sms_opt_in/new.html.erb_spec.rb": 0.07783190200000001, + "spec/views/two_factor_authentication/totp_verification/show.html.erb_spec.rb": 0.263983231, + "spec/views/users/backup_code_setup/create.html.erb_spec.rb": 0.27626599, + "spec/views/users/backup_code_setup/reminder.html.erb_spec.rb": 0.059435181000000004, + "spec/views/users/delete/show.html.erb_spec.rb": 0.188219776, + "spec/views/users/edit_phone/remove_phone.html.erb_spec.rb": 0.08591955400000001, + "spec/views/users/passwords/edit.html.erb_spec.rb": 0.07117100700000001, + "spec/views/users/phones/add.html.erb_spec.rb": 0.046923689, + "spec/views/users/piv_cac_authentication_setup/new.html.erb_spec.rb": 0.064162238, + "spec/views/users/shared/_otp_delivery_preference_selection.html.erb_spec.rb": 0.07410494200000001, + "spec/views/users/totp_setup/new.html.erb_spec.rb": 0.181947164, + "spec/views/users/two_factor_authentication_setup/index.html.erb_spec.rb": 0.181100083, + "spec/views/users/webauthn_setup/new.html.erb_spec.rb": 0.033767042, + "spec/views/vendor_outage/show.html.erb_spec.rb": 0.028226784999999997 +} From 1df1fb6d0adf4516d568e7b80e0035429ea56b22 Mon Sep 17 00:00:00 2001 From: Eric Gade <105373963+eric-gade@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:08:53 -0400 Subject: [PATCH 21/25] LG-9398 Add Fraud Review / Rejection Timestamp Columns (#8142) * Adding initial db migrations for fraud timestamp fields * Updating Profile model methods to use fraud timestamp columns * Add Profile#fraud_rejection? and #fraud_review_at? * Add references to fraud_rejection_at in specs * Use new timestamp columns in profile_spec Co-authored-by: Alex Bradley Co-authored-by: John Maxwell Co-authored-by: John Skinner Co-authored-by: Amir Reavis-Bey Co-authored-by: Sonia Connolly --- .../idv/personal_key_controller.rb | 4 +-- app/controllers/idv/review_controller.rb | 8 +++--- app/models/profile.rb | 23 +++++++++++++--- ...add_fraud_review_pending_at_to_profiles.rb | 8 ++++++ ...1954_add_fraud_rejection_at_to_profiles.rb | 8 ++++++ db/schema.rb | 4 +++ spec/lib/tasks/review_profile_spec.rb | 1 + spec/models/profile_spec.rb | 27 ++++++++++++------- 8 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 db/primary_migrate/20230403201505_add_fraud_review_pending_at_to_profiles.rb create mode 100644 db/primary_migrate/20230403201954_add_fraud_rejection_at_to_profiles.rb diff --git a/app/controllers/idv/personal_key_controller.rb b/app/controllers/idv/personal_key_controller.rb index 231bc782a46..861e0bc6308 100644 --- a/app/controllers/idv/personal_key_controller.rb +++ b/app/controllers/idv/personal_key_controller.rb @@ -22,8 +22,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: idv_session.profile&.fraud_review_pending?, + fraud_rejection: idv_session.profile&.fraud_rejection?, ) redirect_to next_step end diff --git a/app/controllers/idv/review_controller.rb b/app/controllers/idv/review_controller.rb index 5c648c2f146..8d2dcd0e272 100644 --- a/app/controllers/idv/review_controller.rb +++ b/app/controllers/idv/review_controller.rb @@ -53,16 +53,16 @@ def create analytics.idv_review_complete( success: true, - fraud_review_pending: idv_session.profile.fraud_review_pending, - fraud_rejection: idv_session.profile.fraud_rejection, + fraud_review_pending: idv_session.profile.fraud_review_pending?, + fraud_rejection: idv_session.profile.fraud_rejection?, deactivation_reason: idv_session.profile.deactivation_reason, ) Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer). call(:verified, :view, true) analytics.idv_final( success: true, - fraud_review_pending: idv_session.profile.fraud_review_pending, - fraud_rejection: idv_session.profile.fraud_rejection, + fraud_review_pending: idv_session.profile.fraud_review_pending?, + fraud_rejection: idv_session.profile.fraud_rejection?, deactivation_reason: idv_session.profile.deactivation_reason, ) diff --git a/app/models/profile.rb b/app/models/profile.rb index 66ac064e4ad..03c97de5f22 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -27,6 +27,14 @@ class Profile < ApplicationRecord attr_reader :personal_key + def fraud_review_pending? + !!(fraud_review_pending || fraud_review_pending_at) + end + + def fraud_rejection? + !!(fraud_rejection || fraud_rejection_at) + end + # rubocop:disable Rails/SkipsModelValidations def activate return if fraud_review_pending? || fraud_rejection? @@ -48,7 +56,10 @@ def activate # rubocop:enable Rails/SkipsModelValidations def activate_after_passing_review - update!(fraud_review_pending: false, fraud_rejection: false) + update!( + fraud_review_pending: false, fraud_rejection: false, fraud_review_pending_at: nil, + fraud_rejection_at: nil + ) track_fraud_review_adjudication(decision: 'pass') activate end @@ -58,11 +69,17 @@ def deactivate(reason) end def deactivate_for_fraud_review - update!(active: false, fraud_review_pending: true, fraud_rejection: false) + update!( + active: false, fraud_review_pending: true, fraud_rejection: false, + fraud_review_pending_at: Time.zone.now, fraud_rejection_at: nil + ) end def reject_for_fraud(notify_user:) - update!(active: false, fraud_review_pending: false, fraud_rejection: true) + update!( + active: false, fraud_review_pending: false, fraud_rejection: true, + fraud_review_pending_at: nil, fraud_rejection_at: Time.zone.now + ) track_fraud_review_adjudication( decision: notify_user ? 'manual_reject' : 'automatic_reject', ) diff --git a/db/primary_migrate/20230403201505_add_fraud_review_pending_at_to_profiles.rb b/db/primary_migrate/20230403201505_add_fraud_review_pending_at_to_profiles.rb new file mode 100644 index 00000000000..efd28ec2948 --- /dev/null +++ b/db/primary_migrate/20230403201505_add_fraud_review_pending_at_to_profiles.rb @@ -0,0 +1,8 @@ +class AddFraudReviewPendingAtToProfiles < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + def change + add_column :profiles, :fraud_review_pending_at, :datetime + add_index :profiles, :fraud_review_pending_at, algorithm: :concurrently + end +end diff --git a/db/primary_migrate/20230403201954_add_fraud_rejection_at_to_profiles.rb b/db/primary_migrate/20230403201954_add_fraud_rejection_at_to_profiles.rb new file mode 100644 index 00000000000..b2c6c89b8e1 --- /dev/null +++ b/db/primary_migrate/20230403201954_add_fraud_rejection_at_to_profiles.rb @@ -0,0 +1,8 @@ +class AddFraudRejectionAtToProfiles < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + def change + add_column :profiles, :fraud_rejection_at, :datetime + add_index :profiles, :fraud_rejection_at, algorithm: :concurrently + end +end diff --git a/db/schema.rb b/db/schema.rb index 1110da21d52..7767e896d09 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -446,7 +446,11 @@ t.string "initiating_service_provider_issuer" t.boolean "fraud_review_pending", default: false t.boolean "fraud_rejection", default: false + t.datetime "fraud_review_pending_at" + t.datetime "fraud_rejection_at" + t.index ["fraud_rejection_at"], name: "index_profiles_on_fraud_rejection_at" t.index ["fraud_review_pending"], name: "index_profiles_on_fraud_review_pending" + t.index ["fraud_review_pending_at"], name: "index_profiles_on_fraud_review_pending_at" t.index ["name_zip_birth_year_signature"], name: "index_profiles_on_name_zip_birth_year_signature" t.index ["reproof_at"], name: "index_profiles_on_reproof_at" t.index ["ssn_signature"], name: "index_profiles_on_ssn_signature" diff --git a/spec/lib/tasks/review_profile_spec.rb b/spec/lib/tasks/review_profile_spec.rb index 6aa98f44bd2..895ce545e4b 100644 --- a/spec/lib/tasks/review_profile_spec.rb +++ b/spec/lib/tasks/review_profile_spec.rb @@ -78,6 +78,7 @@ invoke_task expect(user.reload.profiles.first.active).to eq(false) expect(user.reload.profiles.first.fraud_rejection).to eq(true) + expect(user.reload.profiles.first.fraud_rejection_at).to_not be_nil end it 'sends the user an email about their account deactivation' do diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb index f701a0c7b99..e7a9087c22c 100644 --- a/spec/models/profile_spec.rb +++ b/spec/models/profile_spec.rb @@ -264,14 +264,14 @@ end it 'does not activate a profile if under fraud review' do - profile.update(fraud_review_pending: true) + profile.update(fraud_review_pending_at: Time.zone.today - 1.day) profile.activate expect(profile).to_not be_active end it 'does not activate a profile if rejected for fraud' do - profile.update(fraud_rejection: true) + profile.update(fraud_rejection_at: Time.zone.now - 1.day) profile.activate expect(profile).to_not be_active @@ -294,7 +294,10 @@ describe '#activate_after_passing_review' do it 'activates a profile if it passes fraud review' do - profile = create(:profile, user: user, active: false, fraud_review_pending: true) + profile = create( + :profile, user: user, active: false, + fraud_review_pending_at: Time.zone.today - 1.day + ) profile.activate_after_passing_review expect(profile).to be_active @@ -307,7 +310,7 @@ :profile, user: user, active: false, - fraud_review_pending: true, + fraud_review_pending_at: Time.zone.today - 1.day, initiating_service_provider: sp, ) end @@ -353,7 +356,7 @@ :profile, user: user, active: false, - fraud_review_pending: true, + fraud_review_pending_at: Time.zone.today - 1.day, initiating_service_provider: sp, ) expect(profile.initiating_service_provider.irs_attempts_api_enabled?).to be_falsey @@ -371,7 +374,9 @@ expect(profile).to_not be_active expect(profile.fraud_review_pending).to eq(true) + expect(profile.fraud_review_pending_at).to_not be_nil expect(profile.fraud_rejection).to eq(false) + expect(profile.fraud_rejection_at).to be_nil end end @@ -389,7 +394,7 @@ context 'it notifies the user' do let(:profile) do - profile = create(:profile, user: user, fraud_review_pending: true) + profile = create(:profile, user: user, fraud_review_pending_at: Time.zone.today - 1.day) profile.reject_for_fraud(notify_user: true) profile end @@ -401,11 +406,15 @@ it 'sends an email' do expect { profile }.to change(ActionMailer::Base.deliveries, :count).by(1) end + + it 'sets the fraud_rejection_at timestamp' do + expect(profile.fraud_rejection_at).to_not be_nil + end end context 'it does not notify the user' do let(:profile) do - profile = create(:profile, user: user, fraud_review_pending: true) + profile = create(:profile, user: user, fraud_review_pending_at: Time.zone.today - 1.day) profile.reject_for_fraud(notify_user: false) profile end @@ -424,7 +433,7 @@ :profile, user: user, active: false, - fraud_review_pending: true, + fraud_review_pending_at: Time.zone.today - 1.day, initiating_service_provider: sp, ) end @@ -471,7 +480,7 @@ :profile, user: user, active: false, - fraud_review_pending: true, + fraud_review_pending_at: Time.zone.today - 1.day, initiating_service_provider: sp, ) allow(IdentityConfig.store).to receive(:irs_attempt_api_enabled).and_return(true) From 8521fe4a1a9ffd4d7f9e18cb7ffed03bbfb3b81e Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Thu, 6 Apr 2023 11:40:28 -0400 Subject: [PATCH 22/25] Friday test hacking/fix phone rate limiting test (#8116) * Fixed phone number throttling test This test failed because it sometimes said '9 minutes' instead of '10 minutes' We removed `freeze_time`, and changed the test to just check for one of the two possibilities. We don't understand why `freeze_time` wasn't working, but it was simple enough to fix the problem without it. --- spec/features/users/sign_up_spec.rb | 31 +++++++++---------- .../layouts/application.html.erb_spec.rb | 1 + 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index 853bd294c36..69b75b4c49c 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -113,25 +113,22 @@ sign_up_and_set_password - freeze_time do - (IdentityConfig.store.phone_confirmation_max_attempts + 1).times do - visit phone_setup_path - fill_in 'new_phone_form_phone', with: '2025551313' - click_send_one_time_code - end + (IdentityConfig.store.phone_confirmation_max_attempts + 1).times do + visit phone_setup_path + fill_in 'new_phone_form_phone', with: '2025551313' + click_send_one_time_code + end - timeout = distance_of_time_in_words( - Throttle.attempt_window_in_minutes(:phone_confirmation).minutes, - ) + # whether it says '9 minutes' or '10 minutes' depends on how + # slowly the test runs. + throttled_message = I18n.t( + 'errors.messages.phone_confirmation_throttled', + timeout: '(10|9) minutes', + ) - expect(current_path).to eq(authentication_methods_setup_path) - expect(page).to have_content( - I18n.t( - 'errors.messages.phone_confirmation_throttled', - timeout: timeout, - ), - ) - end + expect(current_path).to eq(authentication_methods_setup_path) + + expect(page).to have_content(/#{throttled_message}/) end context 'with js', js: true do diff --git a/spec/views/layouts/application.html.erb_spec.rb b/spec/views/layouts/application.html.erb_spec.rb index 14e1dc36ff7..94d95663f0a 100644 --- a/spec/views/layouts/application.html.erb_spec.rb +++ b/spec/views/layouts/application.html.erb_spec.rb @@ -167,6 +167,7 @@ it 'it render the new relic javascript' do allow(IdentityConfig.store).to receive(:newrelic_browser_key).and_return('foo') allow(IdentityConfig.store).to receive(:newrelic_browser_app_id).and_return('foo') + allow(BrowserSupport).to receive(:supported?).and_return(true) render From 8d72e2070450849e01ab2018fad2eba87d2c7819 Mon Sep 17 00:00:00 2001 From: eileen-nava <80347702+eileen-nava@users.noreply.github.com> Date: Thu, 6 Apr 2023 12:31:40 -0400 Subject: [PATCH 23/25] LG-9237: Collect issuing state on state id page (#8121) * add issuing state dropdown and feature test * display issuing state on verify info page * changelog: Upcoming Features, In-person proofing, Collect issuing state on state id page * correct spec expectation * fix issuing state dropdown to link to state_id_jurisdiction * display correct issuing state on verify page * fix pii attributes comments * add state_id_state to encryptor * respond to feedback --- app/forms/idv/state_id_form.rb | 4 +- app/services/pii/attributes.rb | 6 +- app/views/idv/in_person/state_id.html.erb | 80 +++++++++++++------ app/views/idv/shared/_verify.html.erb | 10 ++- config/locales/idv/en.yml | 1 + config/locales/idv/es.yml | 1 + config/locales/idv/fr.yml | 1 + config/locales/in_person_proofing/en.yml | 18 +++-- config/locales/in_person_proofing/es.yml | 18 +++-- config/locales/in_person_proofing/fr.yml | 19 +++-- lib/idp/constants.rb | 5 +- lib/session_encryptor.rb | 5 +- spec/features/idv/in_person_spec.rb | 23 +++--- .../idv/steps/in_person/verify_step_spec.rb | 14 +++- spec/services/pii/attributes_spec.rb | 2 + spec/support/features/idv_step_helper.rb | 2 +- spec/support/features/in_person_helper.rb | 7 +- 17 files changed, 142 insertions(+), 74 deletions(-) diff --git a/app/forms/idv/state_id_form.rb b/app/forms/idv/state_id_form.rb index 02c8b11a9a1..bbfb252ea16 100644 --- a/app/forms/idv/state_id_form.rb +++ b/app/forms/idv/state_id_form.rb @@ -4,8 +4,8 @@ class StateIdForm include FormStateIdValidator ATTRIBUTES = %i[first_name last_name dob state_id_address1 state_id_address2 - state_id_city state_id_zipcode state_id_jurisdiction state_id_number - same_address_as_id].freeze + state_id_city state_id_zipcode state_id_jurisdiction + state_id_state state_id_number same_address_as_id].freeze attr_accessor(*ATTRIBUTES) diff --git a/app/services/pii/attributes.rb b/app/services/pii/attributes.rb index 53119c0949b..dbe40b20471 100644 --- a/app/services/pii/attributes.rb +++ b/app/services/pii/attributes.rb @@ -10,7 +10,11 @@ module Pii # The user's residential address :address1, :address2, :city, :state, :zipcode, :same_address_as_id, # The address on a user's state-issued ID, which may be different from their residential address - :state_id_address1, :state_id_address2, :state_id_city, :state_id_jurisdiction, :state_id_zipcode, # rubocop:disable Layout/LineLength + :state_id_address1, :state_id_address2, :state_id_city, :state_id_zipcode, + # the state that issued the id, which may be different than the state in the state id address + :state_id_jurisdiction, + # the state in the state id address, which may not be the state that issued the ID + :state_id_state, :ssn, :dob, :phone, *DEPRECATED_PII_ATTRIBUTES ) do diff --git a/app/views/idv/in_person/state_id.html.erb b/app/views/idv/in_person/state_id.html.erb index ca27486a6e3..131a646e320 100644 --- a/app/views/idv/in_person/state_id.html.erb +++ b/app/views/idv/in_person/state_id.html.erb @@ -96,6 +96,21 @@ %>
+ <% if capture_secondary_id_enabled %> +
+ <%= render ValidatedFieldComponent.new( + name: :state_id_jurisdiction, + collection: us_states_territories, + form: f, + hint: t('in_person_proofing.form.state_id.state_id_jurisdiction_hint'), + label: t('in_person_proofing.form.state_id.state_id_jurisdiction'), + label_html: { class: 'usa-label' }, + prompt: t('in_person_proofing.form.state_id.state_id_jurisdiction_prompt'), + required: true, + selected: pii[:state_id_jurisdiction], + ) %> +
+ <% end %>
<%= render ValidatedFieldComponent.new( name: :state_id_number, @@ -110,6 +125,17 @@
<% if capture_secondary_id_enabled %> +

<%= t('in_person_proofing.headings.id_address') %>

+ <%= render ValidatedFieldComponent.new( + name: :state_id_state, + collection: us_states_territories, + form: f, + label: t('in_person_proofing.form.state_id.state_id_state'), + label_html: { class: 'usa-label' }, + prompt: t('in_person_proofing.form.state_id.state_id_state_prompt'), + required: true, + selected: pii[:state_id_state], + ) %> <%= render ValidatedFieldComponent.new( name: :state_id_address1, form: f, @@ -140,19 +166,21 @@ required: true, ) %> <% end %> -
- <%= render ValidatedFieldComponent.new( - name: :state_id_jurisdiction, - collection: us_states_territories, - form: f, - hint: t('in_person_proofing.form.state_id.state_id_jurisdiction_hint'), - label: t('in_person_proofing.form.state_id.state_id_jurisdiction'), - label_html: { class: 'usa-label' }, - prompt: t('in_person_proofing.form.state_id.state_id_jurisdiction_prompt'), - required: true, - selected: pii[:state_id_jurisdiction], - ) %> -
+ <% unless capture_secondary_id_enabled %> +
+ <%= render ValidatedFieldComponent.new( + name: :state_id_jurisdiction, + collection: us_states_territories, + form: f, + hint: t('in_person_proofing.form.state_id.state_id_state_hint'), + label: t('in_person_proofing.form.state_id.state_id_jurisdiction'), + label_html: { class: 'usa-label' }, + prompt: t('in_person_proofing.form.state_id.state_id_jurisdiction_prompt'), + required: true, + selected: pii[:state_id_jurisdiction], + ) %> +
+ <% end %> <% if capture_secondary_id_enabled %>
<%= render ValidatedFieldComponent.new( @@ -165,19 +193,19 @@ required: true, ) %>
- <%= render ValidatedFieldComponent.new( - as: :radio_buttons, - checked: pii[:same_address_as_id], - collection: [ - [t('in_person_proofing.form.state_id.same_address_as_id_yes'), true], - [t('in_person_proofing.form.state_id.same_address_as_id_no'), false], - ], - form: f, - label: t('in_person_proofing.form.state_id.same_address_as_id'), - name: :same_address_as_id, - required: true, - wrapper: :uswds_radio_buttons, - ) %> +

<%= t('in_person_proofing.form.state_id.same_address_as_id') %>

+ <%= render ValidatedFieldComponent.new( + as: :radio_buttons, + checked: pii[:same_address_as_id], + collection: [ + [t('in_person_proofing.form.state_id.same_address_as_id_yes'), true], + [t('in_person_proofing.form.state_id.same_address_as_id_no'), false], + ], + form: f, + name: :same_address_as_id, + required: true, + wrapper: :uswds_radio_buttons, + ) %> <% end %> <%= f.submit do %> diff --git a/app/views/idv/shared/_verify.html.erb b/app/views/idv/shared/_verify.html.erb index 5f20a1d332f..7e9ee3d3501 100644 --- a/app/views/idv/shared/_verify.html.erb +++ b/app/views/idv/shared/_verify.html.erb @@ -29,6 +29,12 @@ locals: <%= I18n.l(Date.parse(pii[:dob]), format: I18n.t('time.formats.event_date')) %>
+ <% if !remote_identity_proofing && capture_secondary_id_enabled %> +
+
<%= t('idv.form.issuing_state') %>:
+
<%= pii[:state_id_jurisdiction] %>
+
+ <% end %> <% if !remote_identity_proofing %>
<%= t('idv.form.id_number') %>:
@@ -36,7 +42,7 @@ locals:
<% end %> <% if !remote_identity_proofing && capture_secondary_id_enabled %> -
+
<%= t('idv.form.address1') %>:
<%= pii[:state_id_address1] %>
@@ -50,7 +56,7 @@ locals:
<%= t('idv.form.state') %>:
-
<%= pii[:state_id_jurisdiction] %>
+
<%= pii[:state_id_state] %>
<%= t('idv.form.zipcode') %>:
diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index 5154bc18e20..7a2cb739cbf 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -143,6 +143,7 @@ en: dob: Date of birth first_name: First name id_number: ID number + issuing_state: Issuing state last_name: Last name password: Password ssn: Social Security number diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index 7514b54df91..75d4453a739 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -153,6 +153,7 @@ es: dob: Fecha de nacimiento first_name: Nombre de pila id_number: Número de cédula + issuing_state: Estado emisor last_name: Apellido password: Contraseña ssn: Número de seguridad social diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml index bb94a64a887..e31d216c680 100644 --- a/config/locales/idv/fr.yml +++ b/config/locales/idv/fr.yml @@ -158,6 +158,7 @@ fr: dob: Date de naissance first_name: Prénom id_number: Numéro d’identification + issuing_state: État émetteur last_name: Nom de famille password: Mot de passe ssn: Numéro de sécurité sociale diff --git a/config/locales/in_person_proofing/en.yml b/config/locales/in_person_proofing/en.yml index 47a25a10932..ba75289e7e1 100644 --- a/config/locales/in_person_proofing/en.yml +++ b/config/locales/in_person_proofing/en.yml @@ -91,8 +91,8 @@ en: same_address_choice_yes: This address is on my state-issued ID state_prompt: '- Select -' state_id: - address1: Address - address2: Address line 2 (optional) + address1: Address line 1 + address2: Address line 2 city: City date_hint: day: 'Example: 28' @@ -111,20 +111,24 @@ en: missing_month_day_year: Enter a date of birth range_min_age: You must be over 13 years of age to use %{app_name} range_overflow: Enter a date that is in the past - same_address_as_id: Is your current residential address listed on your state-issued ID? - same_address_as_id_no: No, my current residential address is not listed on my state-issued ID - same_address_as_id_yes: Yes, my current residential address is listed on my state-issued ID - state_id_jurisdiction: State - state_id_jurisdiction_hint: Select the state shown on your ID + same_address_as_id: Do you currently live at the address listed on your state-issued ID? + same_address_as_id_no: No, I live at a different address + same_address_as_id_yes: Yes, I live at the address on my state-issued ID + state_id_jurisdiction: Issuing state + state_id_jurisdiction_hint: This is the state that issued your ID state_id_jurisdiction_prompt: '- Select -' state_id_number: ID number state_id_number_hint: May include letters and numbers + state_id_state: State + state_id_state_hint: Select the state shown on your ID + state_id_state_prompt: '- Select -' zipcode: ZIP Code headings: address: Enter your current residential address barcode: You’re ready to verify your identity in person with %{app_name} cta: Having trouble verifying your ID online? cta_variant: Try verifying your ID in person + id_address: Address on your ID po_search: location: Find a participating Post Office prepare: Verify your identity in person diff --git a/config/locales/in_person_proofing/es.yml b/config/locales/in_person_proofing/es.yml index 33696aa2d5e..9fdf23439d6 100644 --- a/config/locales/in_person_proofing/es.yml +++ b/config/locales/in_person_proofing/es.yml @@ -104,8 +104,8 @@ es: same_address_choice_yes: Esta dirección aparece en mi cédula de identidad emitida por el estado state_prompt: '- Seleccione -' state_id: - address1: Dirección - address2: Línea de dirección 2 (opcional) + address1: Línea de dirección 1 + address2: Línea de dirección 2 city: Ciudad date_hint: day: 'Ejemplo: 28' @@ -125,23 +125,25 @@ es: missing_month_day_year: Introduce una fecha de nacimiento range_min_age: Debe tener más de 13 años para usar %{app_name} range_overflow: Ingrese una fecha que esté en el pasado - same_address_as_id: ¿Tu domicilio actual aparece en tu identificación emitida - por el estado? - same_address_as_id_no: No, mi domicilio actual no aparece en mi identificación - emitida por el estado. - same_address_as_id_yes: Sí, mi domicilio actual aparece en mi identificación - emitida por el estado + same_address_as_id: ¿Vive actualmente en la dirección que figura en su documento + estatal de identidad? + same_address_as_id_no: No, vivo en otra dirección + same_address_as_id_yes: Sí, vivo en la misma dirección state_id_jurisdiction: Estado state_id_jurisdiction_hint: Seleccione el estado que aparece en su cédula state_id_jurisdiction_prompt: '- Seleccione -' state_id_number: Número de cédula state_id_number_hint: Puede incluir letras y números + state_id_state: Estado emisor + state_id_state_hint: Este es el estado que emitió su identificación + state_id_state_prompt: '- Seleccione -' zipcode: Código postal headings: address: Ingresa tu domicilio actual barcode: Está listo para verificar su identidad en persona con %{app_name} cta: ¿Tiene problemas para verificar su documento de identidad en línea? cta_variant: Intente verificar su ID en persona + id_address: Domicilio que consta en su identificación po_search: location: Encuentre una oficina de correos participante prepare: Verifique su identidad en persona diff --git a/config/locales/in_person_proofing/fr.yml b/config/locales/in_person_proofing/fr.yml index a17055815c4..02cc062646b 100644 --- a/config/locales/in_person_proofing/fr.yml +++ b/config/locales/in_person_proofing/fr.yml @@ -104,8 +104,8 @@ fr: same_address_choice_yes: Cette adresse figure sur mon document d’identité nationale state_prompt: '- Sélectionnez -' state_id: - address1: Adresse - address2: Adresse Ligne 2 (optional) + address1: Adresse ligne 1 + address2: Adresse ligne 2 city: Ville date_hint: day: 'Exemple: 28' @@ -125,23 +125,26 @@ fr: missing_month_day_year: Entrez une date de naissance range_min_age: Vous devez avoir plus de 13 ans pour utiliser %{app_name} range_overflow: Entrez une date qui est dans le passé - same_address_as_id: L’adresse de votre domicile actuel figure-t-elle sur votre - carte d’identité délivrée par l’État? - same_address_as_id_no: Non, l’adresse de mon domicile actuel ne figure pas sur - ma carte d’identité délivrée par l’État - same_address_as_id_yes: Oui, l’adresse de mon domicile actuel figure sur ma - carte d’identité délivrée par l’État + same_address_as_id: Vivez-vous actuellement à l’adresse indiquée sur votre pièce + d’identité émise par l’État? + same_address_as_id_no: Non, j’habite à une adresse différente + same_address_as_id_yes: Oui, j’habite à l’adresse indiquée sur ma pièce + d’identité émise par l’État state_id_jurisdiction: État state_id_jurisdiction_hint: Sélectionnez l’État figurant sur votre document d’identité state_id_jurisdiction_prompt: '- Sélectionnez -' state_id_number: Numéro d’identification state_id_number_hint: Peut comprendre des lettres et des chiffres + state_id_state: État émetteur + state_id_state_hint: Il s’agit de l’État qui a émis votre pièce d’identité + state_id_state_prompt: '- Sélectionnez -' zipcode: Code postal headings: address: Indiquez votre adresse résidentielle actuelle barcode: Vous êtes prêt à vérifier votre identité en personne avec %{app_name} cta: Vous avez des difficultés à vérifier votre identité en ligne? cta_variant: Essayez de vérifier votre identité en personne + id_address: Adresse sur votre pièce d’identité po_search: location: Trouver un bureau de poste participant prepare: Vérifiez votre identité en personne diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb index fcf730f4796..35ccd19e4ae 100644 --- a/lib/idp/constants.rb +++ b/lib/idp/constants.rb @@ -82,6 +82,7 @@ module Vendors AAL2 = 2 AAL3 = 3 + MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION = 'ND' MOCK_IDV_APPLICANT = { first_name: 'FAKEY', middle_name: nil, @@ -93,7 +94,7 @@ module Vendors zipcode: '59010', dob: '1938-10-06', state_id_number: '1111111111111', - state_id_jurisdiction: 'ND', + state_id_jurisdiction: MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION, state_id_type: 'drivers_license', state_id_expiration: '2099-12-31', state_id_issued: '2019-12-31', @@ -105,6 +106,7 @@ module Vendors state_id_address2: '2nd Address Line', state_id_city: 'Best City', state_id_zipcode: '12345', + state_id_state: 'VA', ).freeze MOCK_IDV_APPLICANT_WITH_SSN = MOCK_IDV_APPLICANT.merge(ssn: '900-66-1234').freeze @@ -113,5 +115,6 @@ module Vendors MOCK_IDV_APPLICANT_FULL_STATE_ID_JURISDICTION = 'North Dakota' MOCK_IDV_APPLICANT_FULL_STATE = 'Montana' + MOCK_IDV_APPLICANT_FULL_STATE_ID_STATE = 'Virginia' end end diff --git a/lib/session_encryptor.rb b/lib/session_encryptor.rb index a33bf8df85e..c2a924cb6d3 100644 --- a/lib/session_encryptor.rb +++ b/lib/session_encryptor.rb @@ -8,8 +8,9 @@ class SensitiveValueError < StandardError; end MINIMUM_COMPRESS_LIMIT = 300 SENSITIVE_KEYS = [ 'first_name', 'middle_name', 'last_name', 'address1', 'address2', 'city', 'state', 'zipcode', - 'zip_code', 'state_id_address1', 'state_id_address2', 'state_id_city', 'state_id_jurisdiction', - 'state_id_zipcode', 'same_address_as_id', 'dob', 'phone_number', 'phone', 'ssn', + 'zip_code', 'state_id_address1', 'state_id_address2', 'state_id_city', + 'state_id_zipcode', 'state_id_state', 'state_id_jurisdiction', + 'same_address_as_id', 'dob', 'phone_number', 'phone', 'ssn', 'prev_address1', 'prev_address2', 'prev_city', 'prev_state', 'prev_zipcode', 'pii', 'pii_from_doc', 'pii_from_user', 'password', 'personal_key', 'email', 'email_address', 'unconfirmed_phone' diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb index dd8d37d95c7..76c4f85a5ac 100644 --- a/spec/features/idv/in_person_spec.rb +++ b/spec/features/idv/in_person_spec.rb @@ -268,7 +268,7 @@ end # prepare page - expect(page).to have_content(t('in_person_proofing.headings.prepare')) + expect(page).to have_content(t('in_person_proofing.headings.prepare'), wait: 5) click_button t('forms.buttons.back') expect(page).to have_content(t('in_person_proofing.headings.po_search.location')) @@ -592,7 +592,7 @@ complete_location_step(user) expect(page).to have_content( - t('in_person_proofing.headings.prepare'), + t('in_person_proofing.headings.prepare'), wait: 5 ) end @@ -607,7 +607,7 @@ end end - context 'in_person_capture_secondary_id_enabled feature flag enabled', allow_browser_log: true do + shared_examples 'captures address with state id' do let(:user) { user_with_2fa } before(:each) do @@ -619,12 +619,11 @@ complete_location_step(user) expect(page).to have_content( - t('in_person_proofing.headings.prepare'), + t('in_person_proofing.headings.prepare'), wait: 5 ) end - - def it_captures_address_with_state_id - # prepare page + # prepare page + it 'successfully proceeds through the flow' do complete_prepare_step(user) complete_state_id_step(user, same_address_as_id: false, double_address_verification: true) @@ -636,11 +635,11 @@ def it_captures_address_with_state_id t('idv.form.ssn_label_html'), ) end + end + context 'in_person_capture_secondary_id_enabled feature flag enabled', allow_browser_log: true do context 'flag remains enabled' do - it 'captures the address, address line 2, city, state and zip code' do - it_captures_address_with_state_id - end + it_behaves_like 'captures address with state id' end context 'flag is then disabled' do @@ -649,9 +648,7 @@ def it_captures_address_with_state_id and_return(false) end - it 'captures the address, address line 2, city, state and zip code' do - it_captures_address_with_state_id - end + it_behaves_like 'captures address with state id' end end end diff --git a/spec/features/idv/steps/in_person/verify_step_spec.rb b/spec/features/idv/steps/in_person/verify_step_spec.rb index a8288f3c411..d72e72aa289 100644 --- a/spec/features/idv/steps/in_person/verify_step_spec.rb +++ b/spec/features/idv/steps/in_person/verify_step_spec.rb @@ -110,6 +110,10 @@ 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}", + ) expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) expect_good_state_id_address expect(page).to have_content(t('headings.residential_address')) @@ -159,11 +163,19 @@ 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}", + ) expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_ADDRESS1).twice expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_ADDRESS2).twice expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_CITY).twice - expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]).twice + expect(page).to have_text( + Idp::Constants:: + MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:state_id_state], + ).twice + expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]).once expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_ZIPCODE).twice expect(page).to have_content(t('headings.residential_address')) expect(page).to have_content(t('headings.ssn')) diff --git a/spec/services/pii/attributes_spec.rb b/spec/services/pii/attributes_spec.rb index 73deb5a7241..69cdd51c7e8 100644 --- a/spec/services/pii/attributes_spec.rb +++ b/spec/services/pii/attributes_spec.rb @@ -43,6 +43,7 @@ state_id_city: 'Washington', state_id_jurisdiction: 'DC', state_id_zipcode: '20005', + state_id_state: 'NY', ) expect(pii.state_id_address1).to eq('1600 Pennsylvania Avenue') @@ -50,6 +51,7 @@ expect(pii.state_id_city).to eq('Washington') expect(pii.state_id_jurisdiction).to eq('DC') expect(pii.state_id_zipcode).to eq('20005') + expect(pii.state_id_state).to eq('NY') end end diff --git a/spec/support/features/idv_step_helper.rb b/spec/support/features/idv_step_helper.rb index 88f33b7ab49..e1e6f139772 100644 --- a/spec/support/features/idv_step_helper.rb +++ b/spec/support/features/idv_step_helper.rb @@ -116,7 +116,7 @@ def complete_idv_steps_before_step(step, user = user_with_2fa) end def expect_step_indicator_current_step(text) - expect(page).to have_css('.step-indicator__step--current', text: text) + expect(page).to have_css('.step-indicator__step--current', text: text, wait: 5) end private diff --git a/spec/support/features/in_person_helper.rb b/spec/support/features/in_person_helper.rb index 5711c3c0e63..a6eb1f629cc 100644 --- a/spec/support/features/in_person_helper.rb +++ b/spec/support/features/in_person_helper.rb @@ -23,6 +23,7 @@ module InPersonHelper GOOD_STATE = Idp::Constants::MOCK_IDV_APPLICANT_FULL_STATE GOOD_STATE_ID_ADDRESS1 = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:state_id_address1] GOOD_STATE_ID_ADDRESS2 = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:state_id_address2] + GOOD_STATE_ID_STATE = Idp::Constants::MOCK_IDV_APPLICANT_FULL_STATE_ID_STATE GOOD_STATE_ID_CITY = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:city] GOOD_STATE_ID_ZIPCODE = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:zipcode] @@ -43,8 +44,10 @@ def fill_out_state_id_form_ok(double_address_verification: false, same_address_a fill_in t('in_person_proofing.form.state_id.city'), with: GOOD_STATE_ID_CITY fill_in t('in_person_proofing.form.state_id.zipcode'), with: GOOD_STATE_ID_ZIPCODE if same_address_as_id + select GOOD_STATE_ID_STATE, from: t('in_person_proofing.form.state_id.state_id_state') choose t('in_person_proofing.form.state_id.same_address_as_id_yes') else + select GOOD_STATE, from: t('in_person_proofing.form.state_id.state_id_state') choose t('in_person_proofing.form.state_id.same_address_as_id_no') end end @@ -59,11 +62,11 @@ def fill_out_address_form_ok(double_address_verification: false, same_address_as fill_in t('idv.form.city'), with: same_address_as_id ? GOOD_STATE_ID_CITY : GOOD_CITY fill_in t('idv.form.zipcode'), with: same_address_as_id ? GOOD_STATE_ID_ZIPCODE : GOOD_ZIPCODE if same_address_as_id - select GOOD_STATE_ID_JURISDICTION, - from: t('in_person_proofing.form.state_id.state_id_jurisdiction') + select GOOD_STATE_ID_STATE, from: t('idv.form.state') else select GOOD_STATE, from: t('idv.form.state') end + unless double_address_verification choose t('in_person_proofing.form.address.same_address_choice_yes') end From b5dca1856736406233cc41ccd2f96df2a9257c8f Mon Sep 17 00:00:00 2001 From: Tim Bradley <90272033+NavaTim@users.noreply.github.com> Date: Thu, 6 Apr 2023 09:35:52 -0700 Subject: [PATCH 24/25] LG-9237: Send USPS the state instead of jurisdiction from state ID (#8131) [skip changelog] --- app/services/usps_in_person_proofing/enrollment_helper.rb | 2 +- spec/services/usps_in_person_proofing/enrollment_helper_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/usps_in_person_proofing/enrollment_helper.rb b/app/services/usps_in_person_proofing/enrollment_helper.rb index 7bdf92559ef..b3fc7d1ab52 100644 --- a/app/services/usps_in_person_proofing/enrollment_helper.rb +++ b/app/services/usps_in_person_proofing/enrollment_helper.rb @@ -98,7 +98,7 @@ def usps_proofer state_id_address1: :address1, state_id_address2: :address2, state_id_city: :city, - state_id_jurisdiction: :state, + state_id_state: :state, state_id_zipcode: :zipcode, }.freeze diff --git a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb index 3dcc9274825..c32a3d399cc 100644 --- a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb +++ b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb @@ -121,7 +121,7 @@ }", city: Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:state_id_city], state: Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[ - :state_id_jurisdiction + :state_id_state ], zip_code: Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[:state_id_zipcode], ) From 18b73a72ab1a4f71ff5b34bfa95cfbc7b3644c4a Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 6 Apr 2023 09:52:05 -0700 Subject: [PATCH 25/25] Add a total users across all SPs to user count reports (LG-9408) (#8135) * Update sp-user-counts-report to count users as either IAL1 or IAL2 * Update sp-active-user-counts to count users as either IAL1 and IAL2, not both * Switch to nil issuer instead of LOGIN_ALL - Minimize chances of a colliding with actual issuer in the future changelog: Internal, Reporting, Add total count acros all SP to user reports --- app/jobs/reports/sp_active_users_report.rb | 6 +- app/jobs/reports/sp_user_counts_report.rb | 2 +- .../db/identity/sp_active_user_counts.rb | 82 ++++-- app/services/db/identity/sp_user_counts.rb | 35 ++- spec/factories/service_provider_identities.rb | 4 + .../reports/sp_active_users_report_spec.rb | 4 +- .../reports/sp_active_users_report_spec.rb | 59 ++++- .../reports/sp_user_counts_report_spec.rb | 63 ++++- .../db/identity/sp_active_user_counts_spec.rb | 234 +++++++++++------- .../db/identity/sp_user_counts_spec.rb | 92 +++++-- 10 files changed, 414 insertions(+), 167 deletions(-) diff --git a/app/jobs/reports/sp_active_users_report.rb b/app/jobs/reports/sp_active_users_report.rb index 184ae184140..4034e6d86b2 100644 --- a/app/jobs/reports/sp_active_users_report.rb +++ b/app/jobs/reports/sp_active_users_report.rb @@ -14,9 +14,11 @@ class SpActiveUsersReport < BaseReport # The report will run for the entire fiscal year that ended the day before rather than for the # partial day of October 1st in the current fiscal year. def perform(date) + range = reporting_range(date) + results = transaction_with_timeout do - range = reporting_range(date) - Db::Identity::SpActiveUserCounts.call(range.begin, range.end) + Db::Identity::SpActiveUserCounts.by_issuer(range.begin, range.end) + + Db::Identity::SpActiveUserCounts.overall(range.begin, range.end) end save_report(REPORT_NAME, results.to_json, extension: 'json') end diff --git a/app/jobs/reports/sp_user_counts_report.rb b/app/jobs/reports/sp_user_counts_report.rb index f40e9c3d319..2304f951d55 100644 --- a/app/jobs/reports/sp_user_counts_report.rb +++ b/app/jobs/reports/sp_user_counts_report.rb @@ -6,7 +6,7 @@ class SpUserCountsReport < BaseReport def perform(_date) user_counts = transaction_with_timeout do - Db::Identity::SpUserCounts.call + Db::Identity::SpUserCounts.by_issuer + Db::Identity::SpUserCounts.overall end save_report(REPORT_NAME, user_counts.to_json, extension: 'json') diff --git a/app/services/db/identity/sp_active_user_counts.rb b/app/services/db/identity/sp_active_user_counts.rb index bcb70bebf03..44b6034e2b3 100644 --- a/app/services/db/identity/sp_active_user_counts.rb +++ b/app/services/db/identity/sp_active_user_counts.rb @@ -1,38 +1,74 @@ module Db module Identity class SpActiveUserCounts - def self.call(start, finish = Time.zone.now) + def self.by_issuer(start, finish = Time.zone.now) params = { start: ActiveRecord::Base.connection.quote(start), finish: ActiveRecord::Base.connection.quote(finish), } + sql = format(<<~SQL, params) SELECT - service_providers.issuer, - MAX(app_id) AS app_id, - CAST(SUM(total_ial1_active) AS INTEGER) AS total_ial1_active, - CAST(SUM(total_ial2_active) AS INTEGER) AS total_ial2_active + identities.service_provider AS issuer + , MAX(service_providers.app_id) AS app_id + , COUNT(*) - SUM( + CASE + WHEN identities.last_ial2_authenticated_at BETWEEN %{start} AND %{finish} THEN 1 + ELSE 0 + END + ) AS total_ial1_active + , SUM( + CASE + WHEN identities.last_ial2_authenticated_at BETWEEN %{start} AND %{finish} THEN 1 + ELSE 0 + END + ) AS total_ial2_active + FROM identities + JOIN service_providers ON service_providers.issuer = identities.service_provider + WHERE + (identities.last_ial1_authenticated_at BETWEEN %{start} AND %{finish}) + OR (identities.last_ial2_authenticated_at BETWEEN %{start} AND %{finish}) + GROUP BY identities.service_provider + SQL + + ActiveRecord::Base.connection.execute(sql).to_a + end + + def self.overall(start, finish = Time.zone.now) + params = { + start: ActiveRecord::Base.connection.quote(start), + finish: ActiveRecord::Base.connection.quote(finish), + } + sql = format(<<~SQL, params) + SELECT + COUNT(*) AS total + , COUNT(by_user.is_ial2) AS ial2_active FROM ( - (SELECT - service_provider AS issuer, - count(*) AS total_ial1_active, - 0 AS total_ial2_active - FROM identities - WHERE %{start} <= last_ial1_authenticated_at AND last_ial1_authenticated_at <= %{finish} - GROUP BY issuer ORDER BY issuer) - UNION - (SELECT - service_provider AS issuer, - 0 AS total_ial1_active, - count(*) AS total_ial2_active + SELECT + identities.user_id + , BOOL_OR(identities.last_ial2_authenticated_at BETWEEN %{start} AND %{finish}) AS is_ial2 FROM identities - WHERE %{start} <= last_ial2_authenticated_at AND last_ial2_authenticated_at <= %{finish} - GROUP BY issuer ORDER BY issuer) - ) AS union_of_ial1_and_ial2_results, service_providers - WHERE union_of_ial1_and_ial2_results.issuer = service_providers.issuer - GROUP BY service_providers.issuer ORDER BY service_providers.issuer + WHERE + (identities.last_ial1_authenticated_at BETWEEN %{start} AND %{finish}) + OR (identities.last_ial2_authenticated_at BETWEEN %{start} AND %{finish}) + GROUP BY identities.user_id + ) by_user SQL - ActiveRecord::Base.connection.execute(sql) + + row = ActiveRecord::Base.connection.execute(sql).to_a.first + + total = row&.fetch('total') || 0 + total_ial2_active = row&.fetch('ial2_active') || 0 + total_ial1_active = total - total_ial2_active + + [ + { + issuer: nil, + app_id: nil, + total_ial1_active:, + total_ial2_active:, + }.transform_keys(&:to_s), + ] end end end diff --git a/app/services/db/identity/sp_user_counts.rb b/app/services/db/identity/sp_user_counts.rb index 47540d5a1aa..8c262230c69 100644 --- a/app/services/db/identity/sp_user_counts.rb +++ b/app/services/db/identity/sp_user_counts.rb @@ -1,7 +1,7 @@ module Db module Identity class SpUserCounts - def self.call + def self.by_issuer sql = <<~SQL SELECT service_provider AS issuer, @@ -13,7 +13,38 @@ def self.call WHERE identities.service_provider = service_providers.issuer GROUP BY identities.service_provider ORDER BY identities.service_provider SQL - ActiveRecord::Base.connection.execute(sql) + ActiveRecord::Base.connection.execute(sql).to_a + end + + def self.overall + sql = <<~SQL + SELECT + COUNT(*) AS num_users + , by_user.is_ial2 + FROM ( + SELECT + identities.user_id + , BOOL_OR(identities.verified_at IS NOT NULL) AS is_ial2 + FROM identities + GROUP BY identities.user_id + ) by_user + GROUP BY by_user.is_ial2 + SQL + + results = ActiveRecord::Base.connection.execute(sql).to_a + + ial1_total = results.find { |r| !r['is_ial2'] }&.fetch('num_users') || 0 + ial2_total = results.find { |r| r['is_ial2'] }&.fetch('num_users') || 0 + + [ + { + issuer: nil, + app_id: nil, + total: ial1_total + ial2_total, + ial1_total:, + ial2_total:, + }.transform_keys(&:to_s), + ] end end end diff --git a/spec/factories/service_provider_identities.rb b/spec/factories/service_provider_identities.rb index 3e43c2e7622..ccaf710ba77 100644 --- a/spec/factories/service_provider_identities.rb +++ b/spec/factories/service_provider_identities.rb @@ -15,4 +15,8 @@ trait :soft_deleted_5m_ago do deleted_at { Time.zone.now - 5.minutes } end + + trait :verified do + verified_at { Time.zone.now } + end end diff --git a/spec/features/reports/sp_active_users_report_spec.rb b/spec/features/reports/sp_active_users_report_spec.rb index 980e0133c6f..8be605bff69 100644 --- a/spec/features/reports/sp_active_users_report_spec.rb +++ b/spec/features/reports/sp_active_users_report_spec.rb @@ -17,7 +17,7 @@ app_id: nil, total_ial1_active: 1, total_ial2_active: 0 }].to_json - expect(Db::Identity::SpActiveUserCounts.call('01-01-2019').to_json).to eq(results) + expect(Db::Identity::SpActiveUserCounts.by_issuer('01-01-2019').to_json).to eq(results) end it 'reports a user as ial2 active for an ial2 sign in' do @@ -36,6 +36,6 @@ app_id: nil, total_ial1_active: 0, total_ial2_active: 1 }].to_json - expect(Db::Identity::SpActiveUserCounts.call('01-01-2019').to_json).to eq(results) + expect(Db::Identity::SpActiveUserCounts.by_issuer('01-01-2019').to_json).to eq(results) end end diff --git a/spec/jobs/reports/sp_active_users_report_spec.rb b/spec/jobs/reports/sp_active_users_report_spec.rb index 17f18d1f179..744287037ad 100644 --- a/spec/jobs/reports/sp_active_users_report_spec.rb +++ b/spec/jobs/reports/sp_active_users_report_spec.rb @@ -7,8 +7,19 @@ let(:issuer2) { 'foo2' } let(:app_id) { 'app' } - it 'is empty' do - expect(subject.perform(Time.zone.today)).to eq('[]') + it 'has overall data' do + report = JSON.parse(subject.perform(Time.zone.today), symbolize_names: true) + + expect(report).to eq( + [ + { + issuer: nil, + app_id: nil, + total_ial1_active: 0, + total_ial2_active: 0, + }, + ], + ) end it 'returns total active user counts per sp broken down by ial1 and ial2' do @@ -31,12 +42,24 @@ user_id: 4, service_provider: issuer, uuid: 'foo4', last_ial2_authenticated_at: authenticated_time ) - result = [{ issuer: issuer, - app_id: app_id, - total_ial1_active: 2, - total_ial2_active: 3 }].to_json + result = [ + { + issuer: issuer, + app_id: app_id, + total_ial1_active: 1, + total_ial2_active: 3, + }, + { + issuer: nil, + app_id: nil, + total_ial1_active: 1, + total_ial2_active: 3, + }, + ] - expect(subject.perform(job_date)).to eq(result) + report = JSON.parse(subject.perform(job_date), symbolize_names: true) + + expect(report).to match_array(result) end it 'when Oct 1, returns total active user counts per sp by ial1 and ial2 for last fiscal year' do @@ -75,12 +98,24 @@ last_ial2_authenticated_at: current_fiscal_year ) - result = [{ issuer: issuer, - app_id: app_id, - total_ial1_active: 2, - total_ial2_active: 3 }].to_json + result = [ + { + issuer: issuer, + app_id: app_id, + total_ial1_active: 0, + total_ial2_active: 3, + }, + { + issuer: nil, + app_id: nil, + total_ial1_active: 0, + total_ial2_active: 3, + }, + ] + + report = JSON.parse(subject.perform(job_date), symbolize_names: true) - expect(subject.perform(job_date)).to eq(result) + expect(report).to match_array(result) end describe '#reporting_range' do diff --git a/spec/jobs/reports/sp_user_counts_report_spec.rb b/spec/jobs/reports/sp_user_counts_report_spec.rb index 32874b74eb5..316b3a6632d 100644 --- a/spec/jobs/reports/sp_user_counts_report_spec.rb +++ b/spec/jobs/reports/sp_user_counts_report_spec.rb @@ -1,25 +1,66 @@ require 'rails_helper' -describe Reports::SpUserCountsReport do +RSpec.describe Reports::SpUserCountsReport do subject { described_class.new } let(:issuer) { 'foo' } let(:app_id) { 'app_id' } - it 'is empty' do - expect(subject.perform(Time.zone.today)).to eq('[]') + it 'has overall data' do + report = JSON.parse(subject.perform(Time.zone.today), symbolize_names: true) + + expect(report).to eq( + [ + { + issuer: nil, + total: 0, + ial1_total: 0, + ial2_total: 0, + app_id: nil, + }, + ], + ) end it 'returns the total user counts per sp broken down by ial1 and ial2' do - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id) - ServiceProviderIdentity.create(user_id: 1, service_provider: issuer, uuid: 'foo1') - ServiceProviderIdentity.create(user_id: 2, service_provider: issuer, uuid: 'foo2') - ServiceProviderIdentity.create( - user_id: 3, service_provider: issuer, uuid: 'foo3', - verified_at: Time.zone.now + create(:service_provider, issuer: issuer, app_id: app_id) + create(:service_provider_identity, user_id: 1, service_provider: issuer) + create(:service_provider_identity, user_id: 2, service_provider: issuer) + create( + :service_provider_identity, :verified, user_id: 3, service_provider: issuer ) - result = [{ issuer: issuer, total: 3, ial1_total: 2, ial2_total: 1, app_id: app_id }].to_json - expect(subject.perform(Time.zone.today)).to eq(result) + issuer2 = 'issuer2' + app_id2 = 'appid2' + create(:service_provider, issuer: issuer2, app_id: app_id2) + create(:service_provider_identity, user_id: 1, service_provider: issuer2) + + expected = [ + { + issuer: issuer, + total: 3, + ial1_total: 2, + ial2_total: 1, + app_id: app_id, + }, + { + issuer: issuer2, + total: 1, + ial1_total: 1, + ial2_total: 0, + app_id: app_id2, + }, + { + issuer: nil, + total: 3, + ial1_total: 2, + ial2_total: 1, + app_id: nil, + }, + ] + + result = JSON.parse(subject.perform(Time.zone.today), symbolize_names: true) + + expect(result).to match_array(expected) end end diff --git a/spec/services/db/identity/sp_active_user_counts_spec.rb b/spec/services/db/identity/sp_active_user_counts_spec.rb index 87cf357b557..93f2edd2af0 100644 --- a/spec/services/db/identity/sp_active_user_counts_spec.rb +++ b/spec/services/db/identity/sp_active_user_counts_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Db::Identity::SpActiveUserCounts do +RSpec.describe Db::Identity::SpActiveUserCounts do subject { described_class } let(:fiscal_start_date) { 1.year.ago } @@ -8,96 +8,156 @@ let(:issuer2) { 'foo2' } let(:app_id1) { 'app1' } let(:app_id2) { 'app2' } + let(:now) { Time.zone.now } - it 'is empty' do - expect(subject.call(fiscal_start_date).ntuples).to eq(0) - end + describe '.by_issuer' do + it 'is empty' do + expect(subject.by_issuer(fiscal_start_date).size).to eq(0) + end - it 'returns total active user counts per sp broken down by ial1 and ial2 for ial1 only sps' do - now = Time.zone.now - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') - ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') - ServiceProviderIdentity.create( - user_id: 1, service_provider: issuer, uuid: 'foo1', - last_ial1_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 2, service_provider: issuer, uuid: 'foo2', - last_ial1_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 3, service_provider: issuer2, uuid: 'foo3', - last_ial1_authenticated_at: now - ) - result = { issuer: issuer, app_id: app_id1, total_ial1_active: 2, total_ial2_active: 0 }.to_json - result2 = { issuer: issuer2, - app_id: app_id2, - total_ial1_active: 1, - total_ial2_active: 0 }.to_json - - tuples = subject.call(fiscal_start_date) - expect(tuples.ntuples).to eq(2) - expect(tuples[0].to_json).to eq(result) - expect(tuples[1].to_json).to eq(result2) - end + it 'returns total active user counts per sp broken down by ial1 and ial2 for ial1 only sps' do + ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') + ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') + ServiceProviderIdentity.create( + user_id: 1, service_provider: issuer, uuid: 'foo1', + last_ial1_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 2, service_provider: issuer, uuid: 'foo2', + last_ial1_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 3, service_provider: issuer2, uuid: 'foo3', + last_ial1_authenticated_at: now + ) + result = { issuer: issuer, + app_id: app_id1, + total_ial1_active: 2, + total_ial2_active: 0 }.to_json + result2 = { issuer: issuer2, + app_id: app_id2, + total_ial1_active: 1, + total_ial2_active: 0 }.to_json + + tuples = subject.by_issuer(fiscal_start_date) + expect(tuples.size).to eq(2) + expect(tuples[0].to_json).to eq(result) + expect(tuples[1].to_json).to eq(result2) + end + + it 'returns total active user counts per sp broken down by ial1 and ial2 for ial2 only sps' do + ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') + ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') + ServiceProviderIdentity.create( + user_id: 1, service_provider: issuer, uuid: 'foo1', + last_ial2_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 2, service_provider: issuer, uuid: 'foo2', + last_ial2_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 3, service_provider: issuer2, uuid: 'foo3', + last_ial2_authenticated_at: now + ) + result = { issuer: issuer, + app_id: app_id1, + total_ial1_active: 0, + total_ial2_active: 2 }.to_json + result2 = { issuer: issuer2, + app_id: app_id2, + total_ial1_active: 0, + total_ial2_active: 1 }.to_json + + tuples = subject.by_issuer(fiscal_start_date) + expect(tuples.size).to eq(2) + expect(tuples[0].to_json).to eq(result) + expect(tuples[1].to_json).to eq(result2) + end + + it 'returns total active user counts per sp broken down by ial1 and ial2 for ial1 ial2 sps' do + ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') + ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') + ServiceProviderIdentity.create( + user_id: 1, service_provider: issuer, uuid: 'foo1', + last_ial1_authenticated_at: now, last_ial2_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 2, service_provider: issuer, uuid: 'foo2', + last_ial1_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 3, service_provider: issuer2, uuid: 'foo3', + last_ial1_authenticated_at: now, last_ial2_authenticated_at: now + ) + ServiceProviderIdentity.create( + user_id: 4, service_provider: issuer2, uuid: 'foo4', + last_ial2_authenticated_at: now + ) + result = { issuer: issuer, + app_id: app_id1, + total_ial1_active: 1, + total_ial2_active: 1 }.to_json + result2 = { issuer: issuer2, + app_id: app_id2, + total_ial1_active: 0, + total_ial2_active: 2 }.to_json - it 'returns total active user counts per sp broken down by ial1 and ial2 for ial2 only sps' do - now = Time.zone.now - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') - ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') - ServiceProviderIdentity.create( - user_id: 1, service_provider: issuer, uuid: 'foo1', - last_ial2_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 2, service_provider: issuer, uuid: 'foo2', - last_ial2_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 3, service_provider: issuer2, uuid: 'foo3', - last_ial2_authenticated_at: now - ) - result = { issuer: issuer, app_id: app_id1, total_ial1_active: 0, total_ial2_active: 2 }.to_json - result2 = { issuer: issuer2, - app_id: app_id2, - total_ial1_active: 0, - total_ial2_active: 1 }.to_json - - tuples = subject.call(fiscal_start_date) - expect(tuples.ntuples).to eq(2) - expect(tuples[0].to_json).to eq(result) - expect(tuples[1].to_json).to eq(result2) + tuples = subject.by_issuer(fiscal_start_date) + expect(tuples.size).to eq(2) + expect(tuples[0].to_json).to eq(result) + expect(tuples[1].to_json).to eq(result2) + end end - it 'returns total active user counts per sp broken down by ial1 and ial2 for ial1 ial2 sps' do - now = Time.zone.now - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: 'app1') - ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: 'app2') - ServiceProviderIdentity.create( - user_id: 1, service_provider: issuer, uuid: 'foo1', - last_ial1_authenticated_at: now, last_ial2_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 2, service_provider: issuer, uuid: 'foo2', - last_ial1_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 3, service_provider: issuer2, uuid: 'foo3', - last_ial1_authenticated_at: now, last_ial2_authenticated_at: now - ) - ServiceProviderIdentity.create( - user_id: 4, service_provider: issuer2, uuid: 'foo4', - last_ial2_authenticated_at: now - ) - result = { issuer: issuer, app_id: app_id1, total_ial1_active: 2, total_ial2_active: 1 }.to_json - result2 = { issuer: issuer2, - app_id: app_id2, - total_ial1_active: 1, - total_ial2_active: 2 }.to_json - - tuples = subject.call(fiscal_start_date) - expect(tuples.ntuples).to eq(2) - expect(tuples[0].to_json).to eq(result) - expect(tuples[1].to_json).to eq(result2) + describe '.overall' do + let(:sp1) { create(:service_provider) } + let(:sp2) { create(:service_provider) } + + it 'has placeholder rows with no data' do + result = subject.overall(fiscal_start_date) + + expect(result.size).to eq(1) + expect(result.first).to eq( + 'issuer' => nil, + 'app_id' => nil, + 'total_ial1_active' => 0, + 'total_ial2_active' => 0, + ) + end + + it 'counts the numbers of users that were ial1 active and ial2 active' do + # ial1 and ial2, counts as ial2 + create( + :service_provider_identity, + user_id: 1, + service_provider_record: sp1, + last_ial1_authenticated_at: now, + ) + create( + :service_provider_identity, + user_id: 1, + service_provider_record: sp2, + last_ial2_authenticated_at: now, + ) + + # ial1 only, counts as ial1 + create( + :service_provider_identity, + user_id: 2, + service_provider_record: sp1, + last_ial1_authenticated_at: now, + ) + + result = subject.overall(fiscal_start_date) + + expect(result.size).to eq(1) + expect(result.first).to eq( + 'issuer' => nil, + 'app_id' => nil, + 'total_ial1_active' => 1, + 'total_ial2_active' => 1, + ) + end end end diff --git a/spec/services/db/identity/sp_user_counts_spec.rb b/spec/services/db/identity/sp_user_counts_spec.rb index bca11a850ca..85c6f998770 100644 --- a/spec/services/db/identity/sp_user_counts_spec.rb +++ b/spec/services/db/identity/sp_user_counts_spec.rb @@ -1,36 +1,74 @@ require 'rails_helper' -describe Db::Identity::SpUserCounts do +RSpec.describe Db::Identity::SpUserCounts do subject { described_class } - let(:issuer) { 'foo' } - let(:app_id) { 'app_id' } - let(:issuer2) { 'foo2' } - let(:app_id2) { 'app_id2' } + describe '.by_issuer' do + let(:issuer) { 'foo' } + let(:app_id) { 'app_id' } + let(:issuer2) { 'foo2' } + let(:app_id2) { 'app_id2' } - it 'is empty' do - expect(subject.call.ntuples).to eq(0) + it 'is empty' do + expect(subject.by_issuer.size).to eq(0) + end + + it 'returns the total user counts per sp broken down by ial1 and ial2' do + ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id) + ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: app_id2) + ServiceProviderIdentity.create(user_id: 1, service_provider: issuer, uuid: 'foo1') + ServiceProviderIdentity.create(user_id: 2, service_provider: issuer, uuid: 'foo2') + ServiceProviderIdentity.create( + user_id: 3, service_provider: issuer, uuid: 'foo3', + verified_at: Time.zone.now + ) + ServiceProviderIdentity.create( + user_id: 4, service_provider: issuer2, uuid: 'foo4', + verified_at: Time.zone.now + ) + result = { issuer: issuer, total: 3, ial1_total: 2, ial2_total: 1, app_id: app_id }.to_json + result2 = { issuer: issuer2, total: 1, ial1_total: 0, ial2_total: 1, app_id: app_id2 }.to_json + + tuples = subject.by_issuer + expect(tuples.size).to eq(2) + expect(tuples[0].to_json).to eq(result) + expect(tuples[1].to_json).to eq(result2) + end end - it 'returns the total user counts per sp broken down by ial1 and ial2' do - ServiceProvider.create(issuer: issuer, friendly_name: issuer, app_id: app_id) - ServiceProvider.create(issuer: issuer2, friendly_name: issuer2, app_id: app_id2) - ServiceProviderIdentity.create(user_id: 1, service_provider: issuer, uuid: 'foo1') - ServiceProviderIdentity.create(user_id: 2, service_provider: issuer, uuid: 'foo2') - ServiceProviderIdentity.create( - user_id: 3, service_provider: issuer, uuid: 'foo3', - verified_at: Time.zone.now - ) - ServiceProviderIdentity.create( - user_id: 4, service_provider: issuer2, uuid: 'foo4', - verified_at: Time.zone.now - ) - result = { issuer: issuer, total: 3, ial1_total: 2, ial2_total: 1, app_id: app_id }.to_json - result2 = { issuer: issuer2, total: 1, ial1_total: 0, ial2_total: 1, app_id: app_id2 }.to_json - - tuples = subject.call - expect(tuples.ntuples).to eq(2) - expect(tuples[0].to_json).to eq(result) - expect(tuples[1].to_json).to eq(result2) + describe '.overall' do + let(:sp1) { create(:service_provider) } + let(:sp2) { create(:service_provider) } + + it 'has zeroes with no data' do + result = subject.overall + expect(result.size).to eq(1) + + expect(result.first).to eq( + 'issuer' => nil, + 'app_id' => nil, + 'total' => 0, + 'ial1_total' => 0, + 'ial2_total' => 0, + ) + end + + it 'aggregates across all issuers' do + create(:service_provider_identity, user_id: 1, service_provider_record: sp1) + create(:service_provider_identity, :verified, user_id: 1, service_provider_record: sp2) + + create(:service_provider_identity, user_id: 2, service_provider_record: sp1) + + result = subject.overall + expect(result.size).to eq(1) + + expect(result.first).to eq( + 'issuer' => nil, + 'app_id' => nil, + 'total' => 2, + 'ial1_total' => 1, + 'ial2_total' => 1, + ) + end end end