diff --git a/app/components/accordion_component.html.erb b/app/components/accordion_component.html.erb index faa3d4a2a71..7a60da3f57c 100644 --- a/app/components/accordion_component.html.erb +++ b/app/components/accordion_component.html.erb @@ -9,9 +9,7 @@ <%= header %> -
-
- <%= content %> -
+
+ <%= content %>
<% end %> diff --git a/app/controllers/concerns/idv/document_capture_concern.rb b/app/controllers/concerns/idv/document_capture_concern.rb index 713fd03552b..4054e20a230 100644 --- a/app/controllers/concerns/idv/document_capture_concern.rb +++ b/app/controllers/concerns/idv/document_capture_concern.rb @@ -49,7 +49,7 @@ def stored_result end def selfie_requirement_met? - !decorated_sp_session.selfie_required? || stored_result.selfie_check_performed? + !decorated_sp_session.biometric_comparison_required? || stored_result.selfie_check_performed? end private diff --git a/app/controllers/concerns/idv_session_concern.rb b/app/controllers/concerns/idv_session_concern.rb index 9857ee11ae5..d1ceafc3858 100644 --- a/app/controllers/concerns/idv_session_concern.rb +++ b/app/controllers/concerns/idv_session_concern.rb @@ -17,7 +17,7 @@ def hybrid_session? end def idv_needed? - user_needs_selfie? || + user_needs_biometric_comparison? || idv_session_user.active_profile.blank? || decorated_sp_session.requested_more_recent_verification? || idv_session_user.reproof_for_irs?(service_provider: current_sp) @@ -66,7 +66,8 @@ def idv_session_user current_user end - def user_needs_selfie? - decorated_sp_session.selfie_required? && !current_user.identity_verified_with_selfie? + def user_needs_biometric_comparison? + decorated_sp_session.biometric_comparison_required? && + !current_user.identity_verified_with_biometric_comparison? end end diff --git a/app/controllers/concerns/idv_step_concern.rb b/app/controllers/concerns/idv_step_concern.rb index 1344a5c8c3b..396dc73fa25 100644 --- a/app/controllers/concerns/idv_step_concern.rb +++ b/app/controllers/concerns/idv_step_concern.rb @@ -111,7 +111,7 @@ def flow_policy def confirm_step_allowed # set it everytime, since user may switch SP - idv_session.selfie_check_required = decorated_sp_session.selfie_required? + idv_session.selfie_check_required = decorated_sp_session.biometric_comparison_required? return if flow_policy.controller_allowed?(controller: self.class) redirect_to url_for_latest_step diff --git a/app/controllers/frontend_log_controller.rb b/app/controllers/frontend_log_controller.rb index fd10cdcf9a6..11efab30328 100644 --- a/app/controllers/frontend_log_controller.rb +++ b/app/controllers/frontend_log_controller.rb @@ -35,7 +35,6 @@ class FrontendLogController < ApplicationController 'IdV: personal key acknowledgment toggled' => :idv_personal_key_acknowledgment_toggled, 'IdV: prepare submitted' => :idv_in_person_prepare_submitted, 'IdV: prepare visited' => :idv_in_person_prepare_visited, - 'IdV: selfie image clicked' => :idv_selfie_image_clicked, 'IdV: switch_back submitted' => :idv_in_person_switch_back_submitted, 'IdV: switch_back visited' => :idv_in_person_switch_back_visited, 'IdV: user clicked sp link on ready to verify page' => :idv_in_person_ready_to_verify_sp_link_clicked, @@ -55,6 +54,7 @@ class FrontendLogController < ApplicationController idv_sdk_selfie_image_capture_failed idv_sdk_selfie_image_capture_opened idv_selfie_image_added + idv_selfie_image_clicked phone_input_country_changed ].freeze diff --git a/app/controllers/idv/address_controller.rb b/app/controllers/idv/address_controller.rb index 0df0196b98f..589b0a78094 100644 --- a/app/controllers/idv/address_controller.rb +++ b/app/controllers/idv/address_controller.rb @@ -59,13 +59,6 @@ def address_from_document def success idv_session.address_edited = address_edited? - idv_session.pii_from_doc = idv_session.pii_from_doc.merge( - address1: @address_form.address1, - address2: @address_form.address2, - city: @address_form.city, - state: @address_form.state, - zipcode: @address_form.zipcode, - ) idv_session.updated_user_address = @address_form.updated_user_address redirect_to idv_verify_info_url end diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 3cca276476b..1801378902e 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -51,7 +51,7 @@ def extra_view_variables skip_doc_auth: idv_session.skip_doc_auth, skip_doc_auth_from_handoff: idv_session.skip_doc_auth_from_handoff, opted_in_to_in_person_proofing: idv_session.opted_in_to_in_person_proofing, - doc_auth_selfie_capture: decorated_sp_session.selfie_required?, + doc_auth_selfie_capture: decorated_sp_session.biometric_comparison_required?, }.merge( acuant_sdk_upgrade_a_b_testing_variables, ) @@ -97,7 +97,7 @@ def analytics_arguments irs_reproofing: irs_reproofing?, redo_document_capture: idv_session.redo_document_capture, skip_hybrid_handoff: idv_session.skip_hybrid_handoff, - liveness_checking_required: decorated_sp_session.selfie_required?, + liveness_checking_required: decorated_sp_session.biometric_comparison_required?, selfie_check_required: idv_session.selfie_check_required, }.merge(ab_test_analytics_buckets) end diff --git a/app/controllers/idv/hybrid_mobile/capture_complete_controller.rb b/app/controllers/idv/hybrid_mobile/capture_complete_controller.rb index 6dd2cef744e..d40671dddb6 100644 --- a/app/controllers/idv/hybrid_mobile/capture_complete_controller.rb +++ b/app/controllers/idv/hybrid_mobile/capture_complete_controller.rb @@ -25,7 +25,7 @@ def analytics_arguments step: 'capture_complete', analytics_id: 'Doc Auth', irs_reproofing: irs_reproofing?, - liveness_checking_required: decorated_sp_session.selfie_required?, + liveness_checking_required: decorated_sp_session.biometric_comparison_required?, }.merge(ab_test_analytics_buckets) end end diff --git a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb index df483aa7e8a..ae138d92096 100644 --- a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb @@ -43,7 +43,7 @@ def extra_view_variables flow_path: 'hybrid', document_capture_session_uuid: document_capture_session_uuid, failure_to_proof_url: return_to_sp_failure_to_proof_url(step: 'document_capture'), - doc_auth_selfie_capture: decorated_sp_session.selfie_required?, + doc_auth_selfie_capture: decorated_sp_session.biometric_comparison_required?, }.merge( acuant_sdk_upgrade_a_b_testing_variables, ) @@ -57,8 +57,8 @@ def analytics_arguments step: 'document_capture', analytics_id: 'Doc Auth', irs_reproofing: irs_reproofing?, - liveness_checking_required: decorated_sp_session.selfie_required?, - selfie_check_required: decorated_sp_session.selfie_required?, + liveness_checking_required: decorated_sp_session.biometric_comparison_required?, + selfie_check_required: decorated_sp_session.biometric_comparison_required?, }.merge( ab_test_analytics_buckets, ) diff --git a/app/controllers/idv/image_uploads_controller.rb b/app/controllers/idv/image_uploads_controller.rb index b5d45f192ba..e9a7105e6ad 100644 --- a/app/controllers/idv/image_uploads_controller.rb +++ b/app/controllers/idv/image_uploads_controller.rb @@ -25,7 +25,7 @@ def image_upload_form uuid_prefix: current_sp&.app_id, irs_attempts_api_tracker: irs_attempts_api_tracker, store_encrypted_images: store_encrypted_images?, - liveness_checking_required: decorated_sp_session.selfie_required?, + liveness_checking_required: decorated_sp_session.biometric_comparison_required?, ) end diff --git a/app/controllers/idv_controller.rb b/app/controllers/idv_controller.rb index e2d41392326..1f0c366e581 100644 --- a/app/controllers/idv_controller.rb +++ b/app/controllers/idv_controller.rb @@ -32,8 +32,8 @@ def activated private def already_verified? - if decorated_sp_session.selfie_required? - return current_user.identity_verified_with_selfie? + if decorated_sp_session.biometric_comparison_required? + return current_user.identity_verified_with_biometric_comparison? end return current_user.active_profile.present? diff --git a/app/controllers/openid_connect/authorization_controller.rb b/app/controllers/openid_connect/authorization_controller.rb index 9c3b3483c15..a40e470c4fd 100644 --- a/app/controllers/openid_connect/authorization_controller.rb +++ b/app/controllers/openid_connect/authorization_controller.rb @@ -30,7 +30,7 @@ def index return redirect_to reactivate_account_url if user_needs_to_reactivate_account? return redirect_to url_for_pending_profile_reason if user_has_pending_profile? return redirect_to idv_url if identity_needs_verification? - return redirect_to idv_url if selfie_needed? + return redirect_to idv_url if biometric_comparison_needed? end return redirect_to sign_up_completed_url if needs_completion_screen_reason link_identity_to_service_provider @@ -127,9 +127,9 @@ def identity_needs_verification? current_user.reproof_for_irs?(service_provider: current_sp) end - def selfie_needed? - decorated_sp_session.selfie_required? && - !current_user.identity_verified_with_selfie? + def biometric_comparison_needed? + decorated_sp_session.biometric_comparison_required? && + !current_user.identity_verified_with_biometric_comparison? end def build_authorize_form_from_params diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb index 83fe68307bd..d7f37c7c2fa 100644 --- a/app/controllers/saml_idp_controller.rb +++ b/app/controllers/saml_idp_controller.rb @@ -36,7 +36,7 @@ def auth return redirect_to reactivate_account_url if user_needs_to_reactivate_account? return redirect_to url_for_pending_profile_reason if user_has_pending_profile? return redirect_to idv_url if identity_needs_verification? - return redirect_to idv_url if selfie_needed? + return redirect_to idv_url if biometric_comparison_needed? end return redirect_to sign_up_completed_url if needs_completion_screen_reason if auth_count == 1 && first_visit_for_sp? @@ -112,9 +112,9 @@ def prompt_for_password_if_ial2_request_and_pii_locked redirect_to capture_password_url end - def selfie_needed? - decorated_sp_session.selfie_required? && - !current_user.identity_verified_with_selfie? + def biometric_comparison_needed? + decorated_sp_session.biometric_comparison_required? && + !current_user.identity_verified_with_biometric_comparison? end def set_devise_failure_redirect_for_concurrent_session_logout diff --git a/app/controllers/sign_up/completions_controller.rb b/app/controllers/sign_up/completions_controller.rb index 19b4c451bb2..ae83a189aaf 100644 --- a/app/controllers/sign_up/completions_controller.rb +++ b/app/controllers/sign_up/completions_controller.rb @@ -6,7 +6,6 @@ class CompletionsController < ApplicationController before_action :confirm_two_factor_authenticated before_action :confirm_identity_verified, if: :identity_proofing_required? - before_action :confirm_selfie_captured, if: :selfie_required? before_action :apply_secure_headers_override, only: [:show, :update] before_action :verify_needs_completions_screen @@ -38,10 +37,6 @@ def confirm_identity_verified redirect_to idv_url if current_user.identity_not_verified? end - def confirm_selfie_captured - redirect_to idv_url if !current_user.identity_verified_with_selfie? - end - def verify_needs_completions_screen return_to_account unless needs_completion_screen_reason end @@ -65,10 +60,6 @@ def ial2_requested? resolved_authn_context_result.identity_proofing_or_ialmax? && current_user.identity_verified? end - def selfie_required? - decorated_sp_session.selfie_required? - end - def return_to_account track_completion_event('account-page') redirect_to account_url diff --git a/app/decorators/null_service_provider_session.rb b/app/decorators/null_service_provider_session.rb index a7024670293..f1c649e76ab 100644 --- a/app/decorators/null_service_provider_session.rb +++ b/app/decorators/null_service_provider_session.rb @@ -47,7 +47,7 @@ def request_url_params {} end - def selfie_required? + def biometric_comparison_required? false end diff --git a/app/decorators/service_provider_session.rb b/app/decorators/service_provider_session.rb index 2cd97bb39be..d2e2c41a1ce 100644 --- a/app/decorators/service_provider_session.rb +++ b/app/decorators/service_provider_session.rb @@ -72,7 +72,7 @@ def sp_issuer sp.issuer end - def selfie_required? + def biometric_comparison_required? !!(FeatureManagement.idv_allow_selfie_check? && sp_session[:biometric_comparison_required]) end diff --git a/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.spec.ts b/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.spec.ts index 01e4231ed77..ca980626684 100644 --- a/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.spec.ts +++ b/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.spec.ts @@ -136,6 +136,27 @@ describe('CaptchaSubmitButtonElement', () => { }); }); }); + + context('when recaptcha fails to load', () => { + beforeEach(() => { + delete (global as any).grecaptcha; + }); + + it('does not prevent default form submission', async () => { + const button = screen.getByRole('button', { name: 'Submit' }); + const form = document.querySelector('form')!; + + let didSubmit = false; + form.addEventListener('submit', (event) => { + expect(event.defaultPrevented).to.equal(false); + event.preventDefault(); + didSubmit = true; + }); + + await userEvent.click(button); + await waitFor(() => expect(didSubmit).to.be.true()); + }); + }); }); }); }); diff --git a/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.ts b/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.ts index c1dce46dab3..3ec45cbb5b0 100644 --- a/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.ts +++ b/app/javascript/packages/captcha-submit-button/captcha-submit-button-element.ts @@ -31,12 +31,12 @@ class CaptchaSubmitButtonElement extends HTMLElement { return this.getAttribute('recaptcha-enterprise') === 'true'; } - get recaptchaClient(): ReCaptchaV2.ReCaptcha { + get recaptchaClient(): ReCaptchaV2.ReCaptcha | undefined { if (this.isRecaptchaEnterprise) { - return grecaptcha.enterprise; + return globalThis.grecaptcha?.enterprise; } - return grecaptcha; + return globalThis.grecaptcha; } submit() { @@ -44,16 +44,16 @@ class CaptchaSubmitButtonElement extends HTMLElement { } invokeChallenge() { - this.recaptchaClient.ready(async () => { + this.recaptchaClient!.ready(async () => { const { recaptchaSiteKey: siteKey, recaptchaAction: action } = this; - const token = await this.recaptchaClient.execute(siteKey!, { action }); + const token = await this.recaptchaClient!.execute(siteKey!, { action }); this.tokenInput.value = token; this.submit(); }); } shouldInvokeChallenge(): boolean { - return !!this.recaptchaSiteKey; + return !!(this.recaptchaSiteKey && this.recaptchaClient); } handleFormSubmit = (event: SubmitEvent) => { diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 0b71c29398c..5b1b2e12e7d 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -437,11 +437,15 @@ function AcuantCapture( return any>(fn: T) => (...args: Parameters) => { if (!isSuppressingClickLogging.current) { - trackEvent(`IdV: ${name} image clicked`, { - source, - ...metadata, - liveness_checking_required: isSelfieCaptureEnabled, - }); + trackEvent( + name === 'selfie' ? 'idv_selfie_image_clicked' : `IdV: ${name} image clicked`, + { + source, + ...metadata, + liveness_checking_required: isSelfieCaptureEnabled, + captureAttempts, + }, + ); } return fn(...args); diff --git a/app/models/user.rb b/app/models/user.rb index 75ef943078b..8411ef8a682 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true class User < ApplicationRecord - # To be dropped in: https://github.com/18F/identity-idp/pull/10429 - self.ignored_columns = [:remember_created_at] - include NonNullUuid include ::NewRelic::Agent::MethodTracer @@ -373,7 +370,7 @@ def identity_verified?(service_provider: nil) active_profile.present? && !reproof_for_irs?(service_provider: service_provider) end - def identity_verified_with_selfie? + def identity_verified_with_biometric_comparison? BIOMETRIC_COMPARISON_IDV_LEVELS.include?(active_profile&.idv_level) end diff --git a/app/presenters/idv/welcome_presenter.rb b/app/presenters/idv/welcome_presenter.rb index c9a3c2c57b2..fbb2a65cbd9 100644 --- a/app/presenters/idv/welcome_presenter.rb +++ b/app/presenters/idv/welcome_presenter.rb @@ -22,6 +22,10 @@ def title t('doc_auth.headings.welcome', sp_name: sp_name) end + def selfie_required? + decorated_sp_session.biometric_comparison_required? + end + def explanation_text(help_link) if first_time_idv? t( diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 8e98b531e89..f7b56a7af0e 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -763,6 +763,7 @@ def idv_back_image_added( # @param [Boolean] isDrop # @param [Boolean] source # @param [Boolean] use_alternate_sdk + # @param [Number] captureAttempts count of image capturing attempts # @param [String] liveness_checking_required Whether or not the selfie is required def idv_back_image_clicked( acuant_sdk_upgrade_a_b_testing_enabled:, @@ -771,6 +772,7 @@ def idv_back_image_clicked( isDrop:, source:, use_alternate_sdk:, + captureAttempts:, liveness_checking_required:, **extra ) @@ -783,6 +785,7 @@ def idv_back_image_clicked( source: source, use_alternate_sdk: use_alternate_sdk, liveness_checking_required: liveness_checking_required, + captureAttempts: captureAttempts, **extra, ) end @@ -1529,6 +1532,7 @@ def idv_front_image_added( # @param [Boolean] isDrop # @param [String] source # @param [String] use_alternate_sdk + # @param [Number] captureAttempts count of image capturing attempts # @param [Boolean] liveness_checking_required def idv_front_image_clicked( acuant_sdk_upgrade_a_b_testing_enabled:, @@ -1537,6 +1541,7 @@ def idv_front_image_clicked( isDrop:, source:, use_alternate_sdk:, + captureAttempts:, liveness_checking_required: nil, **extra ) @@ -1549,6 +1554,7 @@ def idv_front_image_clicked( source: source, use_alternate_sdk: use_alternate_sdk, liveness_checking_required: liveness_checking_required, + captureAttempts: captureAttempts, **extra, ) end @@ -3321,6 +3327,7 @@ def idv_selfie_image_added( # @param [Boolean] isDrop # @param [String] source # @param [String] use_alternate_sdk + # @param [Number] captureAttempts # @param [Boolean] liveness_checking_required # @param [Hash,nil] proofing_components User's proofing components. # @param [String,nil] active_profile_idv_level ID verification level of user's active profile. @@ -3332,6 +3339,7 @@ def idv_selfie_image_clicked( isDrop:, source:, use_alternate_sdk:, + captureAttempts:, liveness_checking_required: nil, proofing_components: nil, active_profile_idv_level: nil, @@ -3346,6 +3354,7 @@ def idv_selfie_image_clicked( isDrop: isDrop, source: source, use_alternate_sdk: use_alternate_sdk, + captureAttempts: captureAttempts, liveness_checking_required: liveness_checking_required, proofing_components: proofing_components, active_profile_idv_level: active_profile_idv_level, diff --git a/app/services/doc_auth/selfie_concern.rb b/app/services/doc_auth/selfie_concern.rb index 0c518e0118c..7db7f82f29a 100644 --- a/app/services/doc_auth/selfie_concern.rb +++ b/app/services/doc_auth/selfie_concern.rb @@ -4,15 +4,15 @@ module DocAuth module SelfieConcern extend ActiveSupport::Concern def selfie_live? - portait_error = get_portrait_error(portrait_match_results) - return true if portait_error.nil? || portait_error.blank? - return !error_is_not_live(portait_error) + portrait_error = get_portrait_error(portrait_match_results) + return true if portrait_error.nil? || portrait_error.blank? + !error_is_not_live(portrait_error) end def selfie_quality_good? - portait_error = get_portrait_error(portrait_match_results) - return true if portait_error.nil? || portait_error.blank? - return !error_is_poor_quality(portait_error) + portrait_error = get_portrait_error(portrait_match_results) + return true if portrait_error.nil? || portrait_error.blank? + !error_is_poor_quality(portrait_error) end def error_is_success(error_message) @@ -20,7 +20,7 @@ def error_is_success(error_message) end def error_is_not_live(error_message) - return error_message == ERROR_TEXTS[:not_live] + error_message == ERROR_TEXTS[:not_live] end def error_is_poor_quality(error_message) @@ -41,7 +41,7 @@ def selfie_check_performed? poor_quality: 'Liveness: PoorQuality', }.freeze - # @param [Object] portrait_match_results trueid portait match info + # @param [Object] portrait_match_results trueid portrait match info def get_portrait_error(portrait_match_results) portrait_match_results&.with_indifferent_access&.dig(:FaceErrorMessage) end diff --git a/app/services/idv/steps/doc_auth_base_step.rb b/app/services/idv/steps/doc_auth_base_step.rb index a59b560c99c..3f8ee103544 100644 --- a/app/services/idv/steps/doc_auth_base_step.rb +++ b/app/services/idv/steps/doc_auth_base_step.rb @@ -58,17 +58,6 @@ def user_id current_user ? current_user.id : user_id_from_token end - def add_cost(token, transaction_id: nil) - Db::SpCost::AddSpCost.call(current_sp, 2, token, transaction_id: transaction_id) - end - - def add_costs(result) - Db::AddDocumentVerificationAndSelfieCosts. - new(user_id: user_id, - service_provider: current_sp). - call(result) - end - def sp_session session.fetch(:sp, {}) end diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index a44ed1340c0..bdd8cc52499 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -167,12 +167,12 @@ es: retrasado in_person_please_call: body: - contact_message_html: Llame al %{contact_number} y facilíteles + contact_message_html: Llame al %{contact_number} y proporcione el código de error %{support_code}. - intro_html: Llame a nuestro centro de atención antes del + intro_html: Llame a nuestro centro de contacto antes del %{date} para seguir verificando su identidad. header: Llámenos - subject: Llame a %{app_name} para proseguir con la verificación de su identidad + subject: Llame a %{app_name} para continuar con la verificación de identidad in_person_ready_to_verify: subject: Está listo para verificar su identidad con %{app_name} en persona in_person_ready_to_verify_reminder: diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index 308db563801..d672155b857 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -173,7 +173,7 @@ fr: intro_html: Appelez notre centre de contact avant le %{date} pour continuer à vérifier votre identité. header: S’il vous plaît, appelez-nous - subject: Appelez %{app_name} pour poursuivre la vérification de votre identité + subject: Appeler %{app_name} afin de poursuivre la vérification de votre identité in_person_ready_to_verify: subject: Vous êtes prêt à vérifier votre identité avec %{app_name} en personne in_person_ready_to_verify_reminder: diff --git a/db/primary_migrate/20240415125124_drop_remember_created_at_from_users.rb b/db/primary_migrate/20240415125124_drop_remember_created_at_from_users.rb new file mode 100644 index 00000000000..18ba3e912ef --- /dev/null +++ b/db/primary_migrate/20240415125124_drop_remember_created_at_from_users.rb @@ -0,0 +1,7 @@ +class DropRememberCreatedAtFromUsers < ActiveRecord::Migration[7.1] + def change + safety_assured do + remove_column :users, :remember_created_at, :datetime + end + end +end diff --git a/db/primary_migrate/20240416165602_add_password_compromised_checked_at_to_users.rb b/db/primary_migrate/20240416165602_add_password_compromised_checked_at_to_users.rb new file mode 100644 index 00000000000..0397c23147e --- /dev/null +++ b/db/primary_migrate/20240416165602_add_password_compromised_checked_at_to_users.rb @@ -0,0 +1,5 @@ +class AddPasswordCompromisedCheckedAtToUsers < ActiveRecord::Migration[7.1] + def change + add_column :users, :password_compromised_checked_at, :datetime, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index ca81df3b182..68cb83a886b 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.1].define(version: 2024_04_11_163520) do +ActiveRecord::Schema[7.1].define(version: 2024_04_16_165602) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" @@ -588,7 +588,6 @@ create_table "users", id: :serial, force: :cascade do |t| t.string "reset_password_token", limit: 255 t.datetime "reset_password_sent_at", precision: nil - t.datetime "remember_created_at", precision: nil t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.datetime "confirmed_at", precision: nil @@ -613,6 +612,7 @@ t.datetime "second_mfa_reminder_dismissed_at" t.datetime "piv_cac_recommended_dismissed_at" t.datetime "sign_in_new_device_at" + t.datetime "password_compromised_checked_at" t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["sign_in_new_device_at"], name: "index_users_on_sign_in_new_device_at" t.index ["uuid"], name: "index_users_on_uuid", unique: true diff --git a/spec/components/accordion_component_spec.rb b/spec/components/accordion_component_spec.rb index df1bec0e45e..9c9d515817f 100644 --- a/spec/components/accordion_component_spec.rb +++ b/spec/components/accordion_component_spec.rb @@ -21,8 +21,8 @@ it 'assigns a unique id' do second_rendered = render_inline(described_class.new) - rendered_id = rendered.css('.usa-accordion__container').first['id'] - second_rendered_id = second_rendered.css('.usa-accordion__container').first['id'] + rendered_id = rendered.css('.usa-accordion__content').first['id'] + second_rendered_id = second_rendered.css('.usa-accordion__content').first['id'] expect(rendered_id).to be_present expect(second_rendered_id).to be_present diff --git a/spec/controllers/concerns/idv/document_capture_concern_spec.rb b/spec/controllers/concerns/idv/document_capture_concern_spec.rb index c77665906c4..5b2375acfc8 100644 --- a/spec/controllers/concerns/idv/document_capture_concern_spec.rb +++ b/spec/controllers/concerns/idv/document_capture_concern_spec.rb @@ -20,7 +20,8 @@ def show context 'selfie checks enabled' do before do decorated_sp_session = instance_double(ServiceProviderSession) - allow(decorated_sp_session).to receive(:selfie_required?).and_return(selfie_required) + allow(decorated_sp_session).to receive(:biometric_comparison_required?). + and_return(biometric_comparison_required) allow(controller).to receive(:decorated_sp_session).and_return(decorated_sp_session) stored_result = instance_double(DocumentCaptureSessionResult) allow(stored_result).to receive(:selfie_check_performed?).and_return(selfie_check_performed) @@ -28,7 +29,7 @@ def show end context 'SP requires biometric_comparison' do - let(:selfie_required) { true } + let(:biometric_comparison_required) { true } context 'selfie check performed' do let(:selfie_check_performed) { true } @@ -46,7 +47,7 @@ def show end context 'SP does not require biometric_comparison' do - let(:selfie_required) { false } + let(:biometric_comparison_required) { false } context 'selfie check performed' do let(:selfie_check_performed) { true } diff --git a/spec/controllers/idv/address_controller_spec.rb b/spec/controllers/idv/address_controller_spec.rb index 2e908237adc..fcfe4edb492 100644 --- a/spec/controllers/idv/address_controller_spec.rb +++ b/spec/controllers/idv/address_controller_spec.rb @@ -63,24 +63,6 @@ end.to change { subject.idv_session.address_edited }.from(nil).to eql(true) end - it 'updates pii_from_doc in idv_session' do - expect do - put :update, params: params - end.to change { subject.idv_session.pii_from_doc } - - expect(subject.idv_session.pii_from_doc).to eql( - pii_from_doc.merge( - { - 'address1' => '1234 Main St', - 'address2' => 'Apt B', - 'city' => 'Beverly Hills', - 'state' => 'CA', - 'zipcode' => '90210', - }, - ), - ) - end - it 'adds updated_user_data to idv_session' do expect do put :update, params: params @@ -127,7 +109,7 @@ end end - xit 'has the correct `address_edited` value when submitted twice with the same data' do + it 'has the correct `address_edited` value when submitted twice with the same data' do put :update, params: params expect(subject.idv_session.address_edited).to eq(true) put :update, params: params diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 6ef47db3842..57e58874208 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -30,7 +30,7 @@ stub_up_to(:hybrid_handoff, idv_session: subject.idv_session) stub_analytics subject.idv_session.document_capture_session_uuid = document_capture_session_uuid - allow(controller.decorated_sp_session).to receive(:selfie_required?). + allow(controller.decorated_sp_session).to receive(:biometric_comparison_required?). and_return(doc_auth_selfie_capture_enabled && sp_selfie_enabled) subject.idv_session.flow_path = flow_path allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) @@ -265,7 +265,8 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(false) allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).with(anything).and_return(false) - allow(subject.decorated_sp_session).to receive(:selfie_required?).and_return(true) + allow(subject.decorated_sp_session).to receive(:biometric_comparison_required?). + and_return(true) end it 'redirect back when accessed from handoff' do subject.idv_session.skip_hybrid_handoff = nil diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 41a2c3549a7..35c86c9fa4c 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -25,7 +25,7 @@ stub_attempts_tracker allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) allow(subject.idv_session).to receive(:service_provider).and_return(service_provider) - allow(subject.decorated_sp_session).to receive(:selfie_required?). + allow(subject.decorated_sp_session).to receive(:biometric_comparison_required?). and_return(sp_selfie_enabled && doc_auth_selfie_capture_enabled) allow(IdentityConfig.store).to receive(:in_person_proofing_enabled) { in_person_proofing } allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { diff --git a/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb index de0d01b980d..4a260d4ff14 100644 --- a/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb +++ b/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb @@ -79,7 +79,12 @@ context 'when a selfie is requested' do before do allow(subject).to receive(:decorated_sp_session). - and_return(double('decorated_session', { selfie_required?: true, sp_name: 'sp' })) + and_return( + double( + 'decorated_session', + { biometric_comparison_required?: true, sp_name: 'sp' }, + ), + ) end context 'when selfie is required by sp session' do it 'requests FE to display selfie' do diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb index 91e3ed90794..7e48898ad57 100644 --- a/spec/controllers/idv/image_uploads_controller_spec.rb +++ b/spec/controllers/idv/image_uploads_controller_spec.rb @@ -334,7 +334,8 @@ let(:selfie_img) { DocAuthImageFixtures.selfie_image_multipart } before do - allow(controller.decorated_sp_session).to receive(:selfie_required?).and_return(true) + allow(controller.decorated_sp_session).to receive(:biometric_comparison_required?). + and_return(true) end it 'returns a successful response and modifies the session' do @@ -1225,7 +1226,7 @@ context 'the frontend requests a selfie' do before do allow(controller).to receive(:decorated_sp_session). - and_return(double('decorated_session', { selfie_required?: true })) + and_return(double('decorated_session', { biometric_comparison_required?: true })) end let(:back_image) { DocAuthImageFixtures.portrait_match_success_yaml } diff --git a/spec/controllers/sign_up/completions_controller_spec.rb b/spec/controllers/sign_up/completions_controller_spec.rb index 17d953e9c5b..6f0b9990838 100644 --- a/spec/controllers/sign_up/completions_controller_spec.rb +++ b/spec/controllers/sign_up/completions_controller_spec.rb @@ -103,33 +103,6 @@ expect(response).to redirect_to(idv_url) end end - - context 'sp requires selfie' do - let(:selfie_capture_enabled) { true } - before do - expect(FeatureManagement).to receive(:idv_allow_selfie_check?). - and_return(selfie_capture_enabled) - subject.session[:sp][:biometric_comparison_required] = 'true' - end - - context 'user does not have a selfie' do - it 'redirects to idv_url' do - get :show - - expect(response).to redirect_to(idv_url) - end - end - - context 'selfie capture not enabled' do - let(:selfie_capture_enabled) { false } - - it 'does not redirect' do - get :show - - expect(response).to render_template :show - end - end - end end context 'IALMax' do diff --git a/spec/decorators/service_provider_session_spec.rb b/spec/decorators/service_provider_session_spec.rb index 49de2b5c8dc..489c90fb87c 100644 --- a/spec/decorators/service_provider_session_spec.rb +++ b/spec/decorators/service_provider_session_spec.rb @@ -190,22 +190,22 @@ it 'returns true when sp biometric_comparison_required is true' do sp_session[:biometric_comparison_required] = true - expect(subject.selfie_required?).to eq(true) + expect(subject.biometric_comparison_required?).to eq(true) end it 'returns true when sp biometric_comparison_required is truthy' do sp_session[:biometric_comparison_required] = 1 - expect(subject.selfie_required?).to eq(true) + expect(subject.biometric_comparison_required?).to eq(true) end it 'returns false when sp biometric_comparison_required is false' do sp_session[:biometric_comparison_required] = false - expect(subject.selfie_required?).to eq(false) + expect(subject.biometric_comparison_required?).to eq(false) end it 'returns false when sp biometric_comparison_required is nil' do sp_session[:biometric_comparison_required] = nil - expect(subject.selfie_required?).to eq(false) + expect(subject.biometric_comparison_required?).to eq(false) end end @@ -214,7 +214,7 @@ it 'returns false' do sp_session[:biometric_comparison_required] = true - expect(subject.selfie_required?).to eq(false) + expect(subject.biometric_comparison_required?).to eq(false) end end end diff --git a/spec/features/idv/doc_auth/redo_document_capture_spec.rb b/spec/features/idv/doc_auth/redo_document_capture_spec.rb index 1d9444bf60d..dd765980c00 100644 --- a/spec/features/idv/doc_auth/redo_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/redo_document_capture_spec.rb @@ -265,7 +265,7 @@ end end - context 'when doc auth is success and portait match fails', allow_browser_log: true do + context 'when doc auth is success and portrait match fails', allow_browser_log: true do before do expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). and_return(true) diff --git a/spec/fixtures/ial2_test_credential_classification_info_no_name.yml b/spec/fixtures/ial2_test_credential_classification_info_no_name.yml index 1452f12ee24..f3bbd617ef9 100644 --- a/spec/fixtures/ial2_test_credential_classification_info_no_name.yml +++ b/spec/fixtures/ial2_test_credential_classification_info_no_name.yml @@ -8,7 +8,7 @@ document: phone: +1 314-555-1212 state_id_jurisdiction: 'ND' doc_auth_result: Passed -failed_alert: [] +failed_alerts: [] classification_info: Front: ClassName: Drivers License diff --git a/spec/fixtures/ial2_test_portrait_match_success.yml b/spec/fixtures/ial2_test_portrait_match_success.yml index 6641f442865..decfc65999d 100644 --- a/spec/fixtures/ial2_test_portrait_match_success.yml +++ b/spec/fixtures/ial2_test_portrait_match_success.yml @@ -12,6 +12,6 @@ document: state_id_type: 'drivers_license' zipcode: '59010' doc_auth_result: Passed -failed_alert: [] +failed_alerts: [] portrait_match_results: FaceMatchResult: Pass diff --git a/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx b/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx index da4a369ab84..931b49bf775 100644 --- a/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx +++ b/spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx @@ -1176,7 +1176,7 @@ describe('document-capture/components/acuant-capture', () => { }), }); - expect(trackEvent).to.be.calledWith('IdV: selfie image clicked'); + expect(trackEvent).to.be.calledWith('idv_selfie_image_clicked'); expect(trackEvent).to.be.calledWith('IdV: Acuant SDK loaded'); expect(trackEvent).to.have.been.calledWith('idv_sdk_selfie_image_capture_opened'); @@ -1193,7 +1193,7 @@ describe('document-capture/components/acuant-capture', () => { }), }); - expect(trackEvent).to.be.calledWith('IdV: selfie image clicked'); + expect(trackEvent).to.be.calledWith('idv_selfie_image_clicked'); expect(trackEvent).to.be.calledWith('IdV: Acuant SDK loaded'); expect(trackEvent).to.have.been.calledWith( @@ -1212,7 +1212,7 @@ describe('document-capture/components/acuant-capture', () => { }), }); - expect(trackEvent).to.be.calledWith('IdV: selfie image clicked'); + expect(trackEvent).to.be.calledWith('idv_selfie_image_clicked'); expect(trackEvent).to.be.calledWith('IdV: Acuant SDK loaded'); expect(trackEvent).to.have.been.calledWith( @@ -1236,7 +1236,7 @@ describe('document-capture/components/acuant-capture', () => { }), }); - expect(trackEvent).to.be.calledWith('IdV: selfie image clicked'); + expect(trackEvent).to.be.calledWith('idv_selfie_image_clicked'); expect(trackEvent).to.be.calledWith('IdV: Acuant SDK loaded'); expect(trackEvent).to.have.been.calledWith( @@ -1402,16 +1402,19 @@ describe('document-capture/components/acuant-capture', () => { source: 'placeholder', isDrop: false, liveness_checking_required: false, + captureAttempts: 1, }); expect(trackEvent).to.have.been.calledWith('IdV: test image clicked', { source: 'button', isDrop: false, liveness_checking_required: false, + captureAttempts: 1, }); expect(trackEvent).to.have.been.calledWith('IdV: test image clicked', { source: 'upload', isDrop: false, liveness_checking_required: false, + captureAttempts: 1, }); }); @@ -1432,6 +1435,7 @@ describe('document-capture/components/acuant-capture', () => { source: 'placeholder', isDrop: true, liveness_checking_required: false, + captureAttempts: 1, }); }); diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 38abe57012f..48515dbeb8f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1415,7 +1415,7 @@ def it_should_not_send_survey end end - describe '#identity_verified_with_selfie?' do + describe '#identity_verified_with_biometric_comparison?' do let(:user) { create(:user) } let(:active_profile) do create( @@ -1428,23 +1428,23 @@ def it_should_not_send_survey it 'returns true if user has an active profile with selfie' do active_profile.idv_level = :unsupervised_with_selfie active_profile.save - expect(user.identity_verified_with_selfie?).to eq true + expect(user.identity_verified_with_biometric_comparison?).to eq true end it 'returns false if user has an active profile without selfie' do - expect(user.identity_verified_with_selfie?).to eq false + expect(user.identity_verified_with_biometric_comparison?).to eq false end it 'return true if user has an active in-person profile' do active_profile.idv_level = :in_person active_profile.save - expect(user.identity_verified_with_selfie?).to eq true + expect(user.identity_verified_with_biometric_comparison?).to eq true end context 'user does not have active profile' do let(:active_profile) { nil } it 'returns false' do - expect(user.identity_verified_with_selfie?).to eq false + expect(user.identity_verified_with_biometric_comparison?).to eq false end end end diff --git a/spec/services/doc_auth/selfie_concern_spec.rb b/spec/services/doc_auth/selfie_concern_spec.rb index 8516c2e9587..36900af849e 100644 --- a/spec/services/doc_auth/selfie_concern_spec.rb +++ b/spec/services/doc_auth/selfie_concern_spec.rb @@ -2,7 +2,7 @@ RSpec.describe DocAuth::SelfieConcern do let(:face_error_message) { '' } - let(:portait_info) do + let(:portrait_info) do { FaceErrorMessage: face_error_message, } @@ -15,7 +15,7 @@ def initialize(portrait_match_results) @portrait_match_results = portrait_match_results end - end.new(portait_info) + end.new(portrait_info) end describe '#selfie_live?' do