From c87078efbae0bc66450eb3b9269fcc45a70397f5 Mon Sep 17 00:00:00 2001 From: KeithNava <134446588+KeithNava@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:41:24 -0500 Subject: [PATCH 01/13] LG-13006: remove all instances of skip_doc_auth and skipDocAuth - step 4.5 of 6 (#11455) * feat: remove all instances of skip_doc_auth and skipDocAuth * changelog: Internal, In-person proofing, remove old skip_doc_auth variable * feat: remove skip_doc_auth from document capture controller * feat: lintfix --- app/controllers/idv/document_capture_controller.rb | 2 -- app/controllers/idv/how_to_verify_controller.rb | 3 --- app/controllers/idv/hybrid_handoff_controller.rb | 6 ++---- app/controllers/idv/socure/document_capture_controller.rb | 1 - .../document-capture/components/document-capture.tsx | 7 +++---- .../components/in-person-prepare-step.tsx | 3 +-- .../packages/document-capture/context/in-person.ts | 7 ------- app/javascript/packs/document-capture.tsx | 3 --- app/views/idv/document_capture/show.html.erb | 1 - .../idv/hybrid_mobile/document_capture/show.html.erb | 1 - app/views/idv/shared/_document_capture.html.erb | 1 - spec/controllers/idv/how_to_verify_controller_spec.rb | 8 -------- spec/controllers/idv/hybrid_handoff_controller_spec.rb | 3 --- spec/support/flow_policy_helper.rb | 1 - spec/views/idv/shared/_document_capture.html.erb_spec.rb | 2 -- 15 files changed, 6 insertions(+), 43 deletions(-) diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 77599c271c7..d9bd17dc517 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -51,7 +51,6 @@ def extra_view_variables flow_path: 'standard', sp_name: decorated_sp_session.sp_name, failure_to_proof_url: return_to_sp_failure_to_proof_url(step: 'document_capture'), - skip_doc_auth: idv_session.skip_doc_auth, skip_doc_auth_from_how_to_verify: idv_session.skip_doc_auth_from_how_to_verify, 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, @@ -71,7 +70,6 @@ def self.step_info # mobile idv_session.skip_doc_auth_from_handoff || idv_session.skip_hybrid_handoff || - idv_session.skip_doc_auth || idv_session.skip_doc_auth_from_how_to_verify || !idv_session.selfie_check_required || # desktop but selfie not required idv_session.desktop_selfie_test_mode_enabled? diff --git a/app/controllers/idv/how_to_verify_controller.rb b/app/controllers/idv/how_to_verify_controller.rb index 17975d9fdda..233d6165d6d 100644 --- a/app/controllers/idv/how_to_verify_controller.rb +++ b/app/controllers/idv/how_to_verify_controller.rb @@ -34,13 +34,11 @@ def update if result.success? if how_to_verify_form_params['selection'] == Idv::HowToVerifyForm::REMOTE idv_session.opted_in_to_in_person_proofing = false - idv_session.skip_doc_auth = false idv_session.skip_doc_auth_from_how_to_verify = false redirect_to idv_hybrid_handoff_url else idv_session.opted_in_to_in_person_proofing = true idv_session.flow_path = 'standard' - idv_session.skip_doc_auth = true idv_session.skip_doc_auth_from_how_to_verify = true redirect_to idv_document_capture_url end @@ -65,7 +63,6 @@ def self.step_info idv_session.service_provider&.in_person_proofing_enabled end, undo_step: ->(idv_session:, user:) { - idv_session.skip_doc_auth = nil idv_session.skip_doc_auth_from_how_to_verify = nil idv_session.opted_in_to_in_person_proofing = nil }, diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 8d41ee040c9..ea69b145fe9 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -47,12 +47,10 @@ def self.selected_remote(idv_session:) if IdentityConfig.store.in_person_proofing_opt_in_enabled && IdentityConfig.store.in_person_proofing_enabled && idv_session.service_provider&.in_person_proofing_enabled - idv_session.skip_doc_auth_from_how_to_verify == false || - idv_session.skip_doc_auth == false + idv_session.skip_doc_auth_from_how_to_verify == false else idv_session.skip_doc_auth_from_how_to_verify.nil? || - idv_session.skip_doc_auth_from_how_to_verify == false || idv_session.skip_doc_auth.nil? || - idv_session.skip_doc_auth == false + idv_session.skip_doc_auth_from_how_to_verify == false end end diff --git a/app/controllers/idv/socure/document_capture_controller.rb b/app/controllers/idv/socure/document_capture_controller.rb index af98f324f79..c018b96750c 100644 --- a/app/controllers/idv/socure/document_capture_controller.rb +++ b/app/controllers/idv/socure/document_capture_controller.rb @@ -89,7 +89,6 @@ def self.step_info # mobile idv_session.skip_doc_auth_from_handoff || idv_session.skip_hybrid_handoff || - idv_session.skip_doc_auth || idv_session.skip_doc_auth_from_how_to_verify || !idv_session.selfie_check_required || idv_session.desktop_selfie_test_mode_enabled?) diff --git a/app/javascript/packages/document-capture/components/document-capture.tsx b/app/javascript/packages/document-capture/components/document-capture.tsx index 201aa85d315..14afc208ce3 100644 --- a/app/javascript/packages/document-capture/components/document-capture.tsx +++ b/app/javascript/packages/document-capture/components/document-capture.tsx @@ -42,7 +42,6 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) { const { inPersonFullAddressEntryEnabled, inPersonURL, - skipDocAuth, skipDocAuthFromHandoff, skipDocAuthFromHowToVerify, } = useContext(InPersonContext); @@ -140,9 +139,9 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) { if (submissionError && formValues) { initialValues = formValues; } - // If the user got here by opting-in to in-person proofing, when skipDocAuthFromHowToVerify === true || skipDocAuth === true, + // If the user got here by opting-in to in-person proofing, when skipDocAuthFromHowToVerify === true // then set steps to inPersonSteps - const isInPersonStepEnabled = skipDocAuthFromHowToVerify || skipDocAuthFromHandoff || skipDocAuth; + const isInPersonStepEnabled = skipDocAuthFromHowToVerify || skipDocAuthFromHandoff; const inPersonSteps: FormStep[] = inPersonURL === undefined ? [] @@ -156,7 +155,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) { } else if (submissionError) { steps = [reviewFormStep, ...inPersonSteps]; } - // If the user got here by opting-in to in-person proofing, when skipDocAuthFromHowToVerify === true || skipDocAuth === true; + // If the user got here by opting-in to in-person proofing, when skipDocAuthFromHowToVerify === true // or opting-in ipp from handoff page, and selfie is required, when skipDocAuthFromHandoff === true // then set stepIndicatorPath to VerifyFlowPath.IN_PERSON const stepIndicatorPath = 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 5e5573d137b..04a12982665 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 @@ -19,7 +19,6 @@ function InPersonPrepareStep({ toPreviousStep }) { inPersonURL, inPersonOutageMessageEnabled, inPersonOutageExpectedUpdateDate, - skipDocAuth, skipDocAuthFromHowToVerify, skipDocAuthFromHandoff, howToVerifyURL, @@ -30,7 +29,7 @@ function InPersonPrepareStep({ toPreviousStep }) { if (skipDocAuthFromHandoff && previousStepURL) { // directly from handoff page forceRedirect(previousStepURL); - } else if ((skipDocAuthFromHowToVerify || skipDocAuth) && howToVerifyURL) { + } else if (skipDocAuthFromHowToVerify && howToVerifyURL) { forceRedirect(howToVerifyURL); } else { toPreviousStep(); diff --git a/app/javascript/packages/document-capture/context/in-person.ts b/app/javascript/packages/document-capture/context/in-person.ts index 09289efda74..2104b0d42f4 100644 --- a/app/javascript/packages/document-capture/context/in-person.ts +++ b/app/javascript/packages/document-capture/context/in-person.ts @@ -42,13 +42,6 @@ export interface InPersonContextProps { */ usStatesTerritories: Array<[string, string]>; - /** - * When skipDocAuth is true and in_person_proofing_opt_in_enabled is true, - * users are directed to the beginning of the IPP flow. This is set to true when - * they choose Opt-in IPP on the new How To Verify page - */ - skipDocAuth?: boolean; - /** * When skipDocAuthFromHowToVerify is true and in_person_proofing_opt_in_enabled is true, * users are directed to the beginning of the IPP flow. This is set to true when diff --git a/app/javascript/packs/document-capture.tsx b/app/javascript/packs/document-capture.tsx index a0c7a5a22d0..e88dd898a76 100644 --- a/app/javascript/packs/document-capture.tsx +++ b/app/javascript/packs/document-capture.tsx @@ -33,7 +33,6 @@ interface AppRootData { idvInPersonUrl?: string; optedInToInPersonProofing: string; securityAndPrivacyHowItWorksUrl: string; - skipDocAuth: string; skipDocAuthFromHowToVerify: string; skipDocAuthFromHandoff: string; howToVerifyURL: string; @@ -106,7 +105,6 @@ const { inPersonOutageExpectedUpdateDate, optedInToInPersonProofing, usStatesTerritories = '', - skipDocAuth, skipDocAuthFromHowToVerify, skipDocAuthFromHandoff, howToVerifyUrl, @@ -138,7 +136,6 @@ render( inPersonFullAddressEntryEnabled: inPersonFullAddressEntryEnabled === 'true', optedInToInPersonProofing: optedInToInPersonProofing === 'true', usStatesTerritories: parsedUsStatesTerritories, - skipDocAuth: skipDocAuth === 'true', skipDocAuthFromHowToVerify: skipDocAuthFromHowToVerify === 'true', skipDocAuthFromHandoff: skipDocAuthFromHandoff === 'true', howToVerifyURL: howToVerifyUrl, diff --git a/app/views/idv/document_capture/show.html.erb b/app/views/idv/document_capture/show.html.erb index e8f61792451..e7fb85d687f 100644 --- a/app/views/idv/document_capture/show.html.erb +++ b/app/views/idv/document_capture/show.html.erb @@ -8,7 +8,6 @@ use_alternate_sdk: use_alternate_sdk, acuant_version: acuant_version, opted_in_to_in_person_proofing: opted_in_to_in_person_proofing, - skip_doc_auth: skip_doc_auth, skip_doc_auth_from_how_to_verify: skip_doc_auth_from_how_to_verify, skip_doc_auth_from_handoff: skip_doc_auth_from_handoff, doc_auth_selfie_capture: doc_auth_selfie_capture, diff --git a/app/views/idv/hybrid_mobile/document_capture/show.html.erb b/app/views/idv/hybrid_mobile/document_capture/show.html.erb index 0e1818c2825..049858e346b 100644 --- a/app/views/idv/hybrid_mobile/document_capture/show.html.erb +++ b/app/views/idv/hybrid_mobile/document_capture/show.html.erb @@ -8,7 +8,6 @@ use_alternate_sdk: use_alternate_sdk, acuant_version: acuant_version, opted_in_to_in_person_proofing: false, - skip_doc_auth: false, skip_doc_auth_from_how_to_verify: false, skip_doc_auth_from_handoff: nil, doc_auth_selfie_capture: doc_auth_selfie_capture, diff --git a/app/views/idv/shared/_document_capture.html.erb b/app/views/idv/shared/_document_capture.html.erb index fc94c9754b9..95724f8576c 100644 --- a/app/views/idv/shared/_document_capture.html.erb +++ b/app/views/idv/shared/_document_capture.html.erb @@ -37,7 +37,6 @@ us_states_territories: @presenter.usps_states_territories, doc_auth_selfie_capture: doc_auth_selfie_capture, doc_auth_selfie_desktop_test_mode: IdentityConfig.store.doc_auth_selfie_desktop_test_mode, - skip_doc_auth: skip_doc_auth, skip_doc_auth_from_how_to_verify: skip_doc_auth_from_how_to_verify, skip_doc_auth_from_handoff: skip_doc_auth_from_handoff, how_to_verify_url: idv_how_to_verify_url, diff --git a/spec/controllers/idv/how_to_verify_controller_spec.rb b/spec/controllers/idv/how_to_verify_controller_spec.rb index 79a87fd0123..4ce5e539e69 100644 --- a/spec/controllers/idv/how_to_verify_controller_spec.rb +++ b/spec/controllers/idv/how_to_verify_controller_spec.rb @@ -36,7 +36,6 @@ get :show expect(Idv::HowToVerifyController.enabled?).to be false - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be_nil expect(response).to redirect_to(idv_hybrid_handoff_url) end @@ -52,7 +51,6 @@ get :show expect(Idv::HowToVerifyController.enabled?).to be false - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be_nil expect(response).to redirect_to(idv_hybrid_handoff_url) end @@ -68,7 +66,6 @@ get :show expect(Idv::HowToVerifyController.enabled?).to be false - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be_nil expect(response).to redirect_to(idv_hybrid_handoff_url) end @@ -81,7 +78,6 @@ expect(Idv::HowToVerifyController.enabled?).to be true expect(subject.idv_session.service_provider.in_person_proofing_enabled).to be true - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be_nil expect(response).to render_template :show end @@ -116,7 +112,6 @@ it 'renders the show template' do get :show - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be_nil expect(response).to render_template :show end @@ -166,7 +161,6 @@ put :update, params: params expect(flash[:error]).not_to be_present - expect(subject.idv_session.skip_doc_auth).to be_nil expect(subject.idv_session.opted_in_to_in_person_proofing).to be_nil end end @@ -219,7 +213,6 @@ it 'sets skip doc auth on idv session to false and redirects to hybrid handoff' do put :update, params: params - expect(subject.idv_session.skip_doc_auth).to be false expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be false expect(response).to redirect_to(idv_hybrid_handoff_url) end @@ -245,7 +238,6 @@ it 'sets skip doc auth on idv session to true and redirects to document capture' do put :update, params: params - expect(subject.idv_session.skip_doc_auth).to be true expect(subject.idv_session.skip_doc_auth_from_how_to_verify).to be true expect(response).to redirect_to(idv_document_capture_url) end diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 65d5c6c9300..bb3a377d53b 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -206,7 +206,6 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). and_return(false) - subject.idv_session.skip_doc_auth = nil subject.idv_session.skip_doc_auth_from_how_to_verify = nil end @@ -230,7 +229,6 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). and_return(false) - subject.idv_session.skip_doc_auth = true subject.idv_session.skip_doc_auth_from_how_to_verify = true subject.idv_session.skip_hybrid_handoff = true end @@ -245,7 +243,6 @@ context 'opt in ipp is not available on service provider' do before do subject.idv_session.service_provider.in_person_proofing_enabled = false - subject.idv_session.skip_doc_auth = nil subject.idv_session.skip_doc_auth_from_how_to_verify = nil end diff --git a/spec/support/flow_policy_helper.rb b/spec/support/flow_policy_helper.rb index b87810148c3..7297af19a16 100644 --- a/spec/support/flow_policy_helper.rb +++ b/spec/support/flow_policy_helper.rb @@ -14,7 +14,6 @@ def stub_step(key:, idv_session:) when :agreement idv_session.idv_consent_given_at = Time.zone.now.to_s when :how_to_verify - idv_session.skip_doc_auth = false idv_session.skip_doc_auth_from_how_to_verify = false when :hybrid_handoff idv_session.flow_path = 'standard' diff --git a/spec/views/idv/shared/_document_capture.html.erb_spec.rb b/spec/views/idv/shared/_document_capture.html.erb_spec.rb index ceddb2af071..d15ab4aa988 100644 --- a/spec/views/idv/shared/_document_capture.html.erb_spec.rb +++ b/spec/views/idv/shared/_document_capture.html.erb_spec.rb @@ -15,7 +15,6 @@ let(:selfie_capture_enabled) { true } let(:acuant_version) { '1.3.3.7' } - let(:skip_doc_auth) { false } let(:skip_doc_auth_from_how_to_verify) { false } let(:skip_doc_auth_from_handoff) { false } let(:opted_in_to_in_person_proofing) { false } @@ -52,7 +51,6 @@ use_alternate_sdk: use_alternate_sdk, acuant_version: acuant_version, doc_auth_selfie_capture: selfie_capture_enabled, - skip_doc_auth: skip_doc_auth, skip_doc_auth_from_how_to_verify: skip_doc_auth_from_how_to_verify, skip_doc_auth_from_handoff: skip_doc_auth_from_handoff, opted_in_to_in_person_proofing: opted_in_to_in_person_proofing, From fd0001db6a3aee072101e87c7611730704ec42ef Mon Sep 17 00:00:00 2001 From: Andrew Duthie <1779930+aduth@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:42:38 -0500 Subject: [PATCH 02/13] LG-14814: Entering TOTP code in wrong format shows error text mentioning phone (#11521) changelog: Bug Fixes, Authentication Apps, Fix error code for invalid format mentioning code sent to phone --- app/views/idv/otp_verification/show.html.erb | 5 +++++ .../otp_verification/show.html.erb | 5 +++++ config/locales/en.yml | 3 ++- config/locales/es.yml | 3 ++- config/locales/fr.yml | 3 ++- config/locales/zh.yml | 3 ++- .../features/two_factor_authentication/sign_in_spec.rb | 10 +++++----- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/views/idv/otp_verification/show.html.erb b/app/views/idv/otp_verification/show.html.erb index 90e43a76ba9..61442691c2c 100644 --- a/app/views/idv/otp_verification/show.html.erb +++ b/app/views/idv/otp_verification/show.html.erb @@ -24,6 +24,11 @@ code_length: @otp_code_length, optional_prefix: '#', class: 'margin-bottom-5', + field_options: { + error_messages: { + patternMismatch: t('errors.messages.phone_otp_format'), + }, + }, ) %> <%= f.submit t('forms.buttons.submit.default'), class: 'margin-bottom-5' %> <% end %> diff --git a/app/views/two_factor_authentication/otp_verification/show.html.erb b/app/views/two_factor_authentication/otp_verification/show.html.erb index 9f8d015188e..ab64e1e2814 100644 --- a/app/views/two_factor_authentication/otp_verification/show.html.erb +++ b/app/views/two_factor_authentication/otp_verification/show.html.erb @@ -29,6 +29,11 @@ autofocus: true, value: @presenter.code_value, optional_prefix: '#', + field_options: { + error_messages: { + patternMismatch: t('errors.messages.phone_otp_format'), + }, + }, ) %> <%= f.input( :remember_device, diff --git a/config/locales/en.yml b/config/locales/en.yml index e6c366a31ce..f0ba426fa0e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -751,13 +751,14 @@ errors.messages.invalid_voice_number: Invalid phone number. Check that you’ve errors.messages.missing_field: Please fill in this field. errors.messages.no_pending_profile: No profile is waiting for verification errors.messages.not_a_number: is not a number -errors.messages.otp_format: Enter the one-time code sent to your phone. Do not use spaces or special characters. +errors.messages.otp_format: Enter your entire one-time code without spaces or special characters errors.messages.password_incorrect: Incorrect password errors.messages.password_mismatch: Your passwords don’t match errors.messages.personal_key_incorrect: Incorrect personal key errors.messages.phone_carrier: Sorry, we are unable to support that phone carrier at this time. Please select a different number and try again. errors.messages.phone_confirmation_limited: You tried too many times, please try again in %{timeout}. errors.messages.phone_duplicate: This account is already using the phone number you entered as an authenticator. Please use a different phone number. +errors.messages.phone_otp_format: Enter the one-time code sent to your phone. Do not use spaces or special characters. errors.messages.phone_required: Phone number is required errors.messages.phone_unsupported: Sorry, we are unable to send SMS at this time. Please try the phone call option below, or use your personal key. errors.messages.premium_rate_phone: This appears to be a premium rate phone number. Please select a different number and try again. diff --git a/config/locales/es.yml b/config/locales/es.yml index c8dd2efd5f0..9e1e3fe49f0 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -762,13 +762,14 @@ errors.messages.invalid_voice_number: Número de teléfono no válido. Verifique errors.messages.missing_field: Llene este campo. errors.messages.no_pending_profile: No hay ningún perfil en espera de verificación errors.messages.not_a_number: no es un número -errors.messages.otp_format: Introduzca el código de un solo uso enviado a su teléfono. No utilice espacios ni caracteres especiales. +errors.messages.otp_format: Ingrese su código de un solo uso completo, sin espacios ni caracteres especiales. errors.messages.password_incorrect: Contraseña incorrecta errors.messages.password_mismatch: Sus contraseñas no coinciden errors.messages.personal_key_incorrect: Clave personal incorrecta errors.messages.phone_carrier: Lo sentimos, no podemos admitir ese proveedor telefónico por ahora. Seleccione un número diferente e inténtelo de nuevo. errors.messages.phone_confirmation_limited: Lo intentó demasiadas veces; vuelva a intentarlo en %{timeout}. errors.messages.phone_duplicate: Esta cuenta ya usa el número de teléfono que ingresó como autenticador. Use un número de teléfono diferente. +errors.messages.phone_otp_format: Introduzca el código de un solo uso enviado a su teléfono. No utilice espacios ni caracteres especiales. errors.messages.phone_required: Se requiere un número de teléfono. errors.messages.phone_unsupported: Lo sentimos, no podemos enviar un SMS en este momento. Intente la opción de llamada telefónica siguiente, o use su clave personal. errors.messages.premium_rate_phone: Parece que se trata de un número de teléfono de tarifa especial. Seleccione un número diferente e inténtelo de nuevo. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index c078e9c4862..88b3c118e60 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -751,13 +751,14 @@ errors.messages.invalid_voice_number: Numéro de téléphone non valide. Vérifi errors.messages.missing_field: Veuillez remplir ce champ. errors.messages.no_pending_profile: Aucun profil en attente de vérification errors.messages.not_a_number: n’est pas un chiffre -errors.messages.otp_format: Entrez le code à usage unique envoyé sur votre téléphone. N’utilisez pas d’espaces ou de caractères spéciaux. +errors.messages.otp_format: Saisissez l’intégralité de votre code à usage unique sans espaces ni caractères spéciaux errors.messages.password_incorrect: Mot de passe incorrect errors.messages.password_mismatch: Vos mots de passe ne correspondent pas errors.messages.personal_key_incorrect: Clé personnelle incorrecte errors.messages.phone_carrier: Désolé, nous ne ne sommes pas en mesure de prendre en charge cet opérateur téléphonique pour le moment. Veuillez sélectionner un autre numéro et réessayer. errors.messages.phone_confirmation_limited: Vous avez essayé trop de fois, veuillez réessayer dans %{timeout}. errors.messages.phone_duplicate: Ce compte utilise déjà le numéro de téléphone que vous avez saisi en tant qu’authentifiant. Veuillez utiliser un numéro de téléphone différent. +errors.messages.phone_otp_format: Entrez le code à usage unique envoyé sur votre téléphone. N’utilisez pas d’espaces ou de caractères spéciaux. errors.messages.phone_required: Le numéro de téléphone est obligatoire errors.messages.phone_unsupported: Désolé, nous ne sommes pas en mesure d’envoyer des SMS pour le moment. Veuillez essayez l’option d’appel téléphonique ci-dessous ou utilisez votre clé personnelle. errors.messages.premium_rate_phone: Il semble s’agir d’un numéro de téléphone surtaxé. Veuillez sélectionner un autre numéro et réessayer. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 0349e3166ef..6e7f1a67663 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -762,13 +762,14 @@ errors.messages.invalid_voice_number: 电话号码有误。检查一下你是否 errors.messages.missing_field: 请填写这一字段。 errors.messages.no_pending_profile: 没有等待验证的用户资料 errors.messages.not_a_number: 不是数字 -errors.messages.otp_format: 输入发送到你手机的一次性代码。请勿使用空格或特殊字符。 +errors.messages.otp_format: 输入你完整的一次性代码(没有空白或特殊字符) errors.messages.password_incorrect: 密码不对。 errors.messages.password_mismatch: 你的密码不一致 errors.messages.personal_key_incorrect: 个人密钥不对 errors.messages.phone_carrier: 抱歉,我们目前无法支持这一电话运营商。请选择一个不同的号码再试一次。 errors.messages.phone_confirmation_limited: 你尝试了太多次。请在 %{timeout}后再试。 errors.messages.phone_duplicate: 该账户已在使用你输入的电话号码作为身份证实器。请使用一个不同的电话号码。 +errors.messages.phone_otp_format: 输入发送到你手机的一次性代码。请勿使用空格或特殊字符。 errors.messages.phone_required: 电话号码是必需的。 errors.messages.phone_unsupported: 抱歉,我们目前无法发送短信(SMS)。请尝试下面的接听电话选项或者使用你的个人密钥。 errors.messages.premium_rate_phone: 这好像是一个高价电话号码。请选择一个不同的号码再试一次。 diff --git a/spec/features/two_factor_authentication/sign_in_spec.rb b/spec/features/two_factor_authentication/sign_in_spec.rb index 06f09bf1949..437b5b54985 100644 --- a/spec/features/two_factor_authentication/sign_in_spec.rb +++ b/spec/features/two_factor_authentication/sign_in_spec.rb @@ -329,29 +329,29 @@ def attempt_to_bypass_2fa # Invalid: Unsupported characters input.fill_in with: 'BADBAD' click_submit_default - expect(page).to have_content(t('errors.messages.otp_format')) + expect(page).to have_content(t('errors.messages.phone_otp_format')) # Invalid: Not enough characters, with prefix fill_in t('components.one_time_code_input.label'), with: '#12345' click_submit_default - expect(page).to have_content(t('errors.messages.otp_format')) + expect(page).to have_content(t('errors.messages.phone_otp_format')) # Invalid: Not enough characters, without prefix fill_in t('components.one_time_code_input.label'), with: '12345' click_submit_default - expect(page).to have_content(t('errors.messages.otp_format')) + expect(page).to have_content(t('errors.messages.phone_otp_format')) # Valid: Enough characters, with prefix input.fill_in with: '#123456' expect(input.value).to eq('#123456') page.evaluate_script('document.activeElement.closest("form").reportValidity()') - expect(page).not_to have_content(t('errors.messages.otp_format')) + expect(page).not_to have_content(t('errors.messages.phone_otp_format')) # Valid: Enough characters, without prefix input.fill_in with: '123456' expect(input.value).to eq('123456') page.evaluate_script('document.activeElement.closest("form").reportValidity()') - expect(page).not_to have_content(t('errors.messages.otp_format')) + expect(page).not_to have_content(t('errors.messages.phone_otp_format')) end scenario 'the user changes delivery method' do From e9e1524e19a2eb27913ae4606f7e917fb230107f Mon Sep 17 00:00:00 2001 From: Samatha Dondeti Date: Mon, 18 Nov 2024 14:14:10 -0800 Subject: [PATCH 03/13] lg-15077 update sensitivetag (#11514) * lg-15077 update sensitivetag changelog: Internal, Reporting,remove system tbale and updatesentitive missed tags * migration comment append --- app/jobs/data_warehouse/daily_sensitive_column_job.rb | 3 ++- .../20241115215510_update_sensitive_columns_comments.rb | 7 +++++++ db/schema.rb | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 db/primary_migrate/20241115215510_update_sensitive_columns_comments.rb diff --git a/app/jobs/data_warehouse/daily_sensitive_column_job.rb b/app/jobs/data_warehouse/daily_sensitive_column_job.rb index d8c6d5d1317..068676e3581 100644 --- a/app/jobs/data_warehouse/daily_sensitive_column_job.rb +++ b/app/jobs/data_warehouse/daily_sensitive_column_job.rb @@ -10,7 +10,8 @@ def perform(timestamp) end def fetch_columns - tables = ActiveRecord::Base.connection.tables - %w[schema_migrations ar_internal_metadata] + tables = ActiveRecord::Base.connection.tables - %w[schema_migrations ar_internal_metadata + awsdms_ddl_audit] sensitive_hash = [] insensitive_hash = [] diff --git a/db/primary_migrate/20241115215510_update_sensitive_columns_comments.rb b/db/primary_migrate/20241115215510_update_sensitive_columns_comments.rb new file mode 100644 index 00000000000..dd1057544dc --- /dev/null +++ b/db/primary_migrate/20241115215510_update_sensitive_columns_comments.rb @@ -0,0 +1,7 @@ +class UpdateSensitiveColumnsComments < ActiveRecord::Migration[7.2] + def change + #change columns to sensitive=true + change_column_comment :phone_number_opt_outs, :phone_fingerprint, from: "sensitive=false", to: "sensitive=true" + change_column_comment :in_person_enrollments, :selected_location_details, from: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=false", to: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=true" + end +end diff --git a/db/schema.rb b/db/schema.rb index 91c174a0880..0c4f93cf8d4 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.2].define(version: 2024_10_29_152408) do +ActiveRecord::Schema[7.2].define(version: 2024_11_15_215510) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" @@ -312,7 +312,7 @@ t.datetime "created_at", null: false, comment: "sensitive=false" t.datetime "updated_at", null: false, comment: "sensitive=false" t.boolean "current_address_matches_id", comment: "True if the user indicates that their current address matches the address on the ID they're bringing to the Post Office. sensitive=false" - t.jsonb "selected_location_details", comment: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=false" + t.jsonb "selected_location_details", comment: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=true" t.string "unique_id", comment: "Unique ID to use with the USPS service sensitive=false" t.datetime "enrollment_established_at", comment: "When the enrollment was successfully established sensitive=false" t.string "issuer", comment: "Issuer associated with the enrollment at time of creation sensitive=false" @@ -417,7 +417,7 @@ create_table "phone_number_opt_outs", force: :cascade do |t| t.string "encrypted_phone", comment: "sensitive=true" - t.string "phone_fingerprint", null: false, comment: "sensitive=false" + t.string "phone_fingerprint", null: false, comment: "sensitive=true" t.string "uuid", comment: "sensitive=false" t.datetime "created_at", null: false, comment: "sensitive=false" t.datetime "updated_at", null: false, comment: "sensitive=false" From f003ab88bec73d33362f4f59167ddf825a031dc7 Mon Sep 17 00:00:00 2001 From: Shane Chesnutt Date: Tue, 19 Nov 2024 11:19:58 -0500 Subject: [PATCH 04/13] Remove allow extra analytics * from dev_rake_spec (#11525) [skip changelog] Remove allow extra analytics * from dev_rake_spec.rb to fix "Unnecessary allowed_extra_analytics" error. --- spec/lib/tasks/dev_rake_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/tasks/dev_rake_spec.rb b/spec/lib/tasks/dev_rake_spec.rb index 4eef8097ab9..84a6e474e72 100644 --- a/spec/lib/tasks/dev_rake_spec.rb +++ b/spec/lib/tasks/dev_rake_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' require 'rake' -RSpec.describe 'dev rake tasks', allowed_extra_analytics: [:*] do +RSpec.describe 'dev rake tasks' do include UspsIppHelper let(:env) do From e04efce8cc6e4c3634661cf83f09a389a8dc1617 Mon Sep 17 00:00:00 2001 From: Doug Price Date: Tue, 19 Nov 2024 11:57:40 -0500 Subject: [PATCH 05/13] LG-14007: wait for Socure result if not present upon return (#11500) LG-14007: wait for Socure result If the user returns from Socure before the job has finished fetching the results, wait and check every 15 seconds. [skip changelog] * new socure wait page * make wait polling interval configurable * set a timeout for waiting for Socure data Note: removes `cancel_establishing_in_person_enrollments` from DocumentCaptureController Spoke with Team Joy and the issue this was addressing is OBE --- .../concerns/idv/document_capture_concern.rb | 5 --- .../idv/document_capture_controller.rb | 2 - .../idv/socure/document_capture_controller.rb | 30 +++++++++++++++ app/services/analytics_events.rb | 38 +++++++++++++++++++ app/services/idv/session.rb | 1 + .../idv/socure/document_capture/wait.html.erb | 30 +++++++++++++++ config/application.yml.default | 2 + lib/identity_config.rb | 2 + .../idv/document_capture_controller_spec.rb | 13 ------- .../document_capture_controller_spec.rb | 26 +++++++++++++ 10 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 app/views/idv/socure/document_capture/wait.html.erb diff --git a/app/controllers/concerns/idv/document_capture_concern.rb b/app/controllers/concerns/idv/document_capture_concern.rb index a92a523599c..e02aa9b69b2 100644 --- a/app/controllers/concerns/idv/document_capture_concern.rb +++ b/app/controllers/concerns/idv/document_capture_concern.rb @@ -89,10 +89,5 @@ def track_document_issuing_state(user, state) doc_auth_log.state = state doc_auth_log.save! end - - def cancel_establishing_in_person_enrollments(user: current_user) - UspsInPersonProofing::EnrollmentHelper. - cancel_stale_establishing_enrollments_for_user(user) - end end end diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index d9bd17dc517..59249f6d9f2 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -35,8 +35,6 @@ def update Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). call('document_capture', :update, true) - cancel_establishing_in_person_enrollments - if result.success? redirect_to idv_ssn_url else diff --git a/app/controllers/idv/socure/document_capture_controller.rb b/app/controllers/idv/socure/document_capture_controller.rb index c018b96750c..8a67b633466 100644 --- a/app/controllers/idv/socure/document_capture_controller.rb +++ b/app/controllers/idv/socure/document_capture_controller.rb @@ -22,6 +22,8 @@ class DocumentCaptureController < ApplicationController skip_before_action :confirm_step_allowed, only: [:update] def show + idv_session.socure_docv_wait_polling_started_at = nil + Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). call('socure_document_capture', :view, true) @@ -65,6 +67,22 @@ def update # Not used in standard flow, here for data consistency with hybrid flow. document_capture_session.confirm_ocr + # If the stored_result is nil, the job fetching the results has not completed. + if stored_result.nil? + analytics.idv_doc_auth_document_capture_polling_wait_visited(**analytics_arguments) + if wait_timed_out? + # flash[:error] = I18n.t('errors.doc_auth.polling_timeout') + # TODO: redirect to try again page LG-14873/14952/15059 + render plain: 'Technical difficulties!!!', status: :ok + else + @refresh_interval = + IdentityConfig.store.doc_auth_socure_wait_polling_refresh_max_seconds + render 'idv/socure/document_capture/wait' + end + + return + end + result = handle_stored_result # TODO: new analytics event? analytics.idv_doc_auth_document_capture_submitted(**result.to_h.merge(analytics_arguments)) @@ -95,6 +113,7 @@ def self.step_info }, undo_step: ->(idv_session:, user:) do idv_session.pii_from_doc = nil + idv_session.socure_docv_wait_polling_started_at = nil idv_session.invalidate_in_person_pii_from_user! end, ) @@ -102,6 +121,17 @@ def self.step_info private + def wait_timed_out? + if idv_session.socure_docv_wait_polling_started_at.nil? + idv_session.socure_docv_wait_polling_started_at = Time.zone.now.to_s + return false + end + start = DateTime.parse(idv_session.socure_docv_wait_polling_started_at) + timeout_period = + IdentityConfig.store.doc_auth_socure_wait_polling_timeout_minutes.minutes || 5.minutes + start + timeout_period < Time.zone.now + end + def analytics_arguments { flow_path: flow_path, diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 240bb5b40ad..f5ace1ca583 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -1240,6 +1240,44 @@ def idv_doc_auth_capture_complete_visited( ) end + # User returns from Socure document capture, but is waiting on a result to be fetched + # @param ["hybrid","standard"] flow_path Document capture user flow + # @param [String] step Current IdV step + # @param [String] analytics_id Current IdV flow identifier + # @param [Boolean] redo_document_capture Whether user is redoing document capture after barcode + # @param [Boolean] skip_hybrid_handoff Whether skipped hybrid handoff A/B test is active + # @param [Boolean] liveness_checking_required Whether facial match check is required + # @param [Boolean] selfie_check_required Whether facial match check is required + # @param [Boolean] opted_in_to_in_person_proofing User opted into in person proofing + # @param [String] acuant_sdk_upgrade_ab_test_bucket A/B test bucket for Acuant document capture + # SDK upgrades + def idv_doc_auth_document_capture_polling_wait_visited( + flow_path:, + step:, + analytics_id:, + redo_document_capture:, + skip_hybrid_handoff:, + liveness_checking_required:, + selfie_check_required:, + opted_in_to_in_person_proofing: nil, + acuant_sdk_upgrade_ab_test_bucket: nil, + **extra + ) + track_event( + :idv_doc_auth_document_capture_polling_wait_visited, + flow_path:, + step:, + analytics_id:, + redo_document_capture:, + skip_hybrid_handoff:, + liveness_checking_required:, + selfie_check_required:, + opted_in_to_in_person_proofing:, + acuant_sdk_upgrade_ab_test_bucket:, + **extra, + ) + end + # User submits IdV document capture step # @param [Boolean] success Whether form validation was successful # @param [Hash] errors Errors resulting from form validation diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index ea2db47c62c..abc19910394 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -32,6 +32,7 @@ class Session skip_doc_auth_from_handoff skip_doc_auth_from_how_to_verify skip_hybrid_handoff + socure_docv_wait_polling_started_at ssn threatmetrix_review_status threatmetrix_session_id diff --git a/app/views/idv/socure/document_capture/wait.html.erb b/app/views/idv/socure/document_capture/wait.html.erb new file mode 100644 index 00000000000..57aa0653147 --- /dev/null +++ b/app/views/idv/socure/document_capture/wait.html.erb @@ -0,0 +1,30 @@ +<%= content_for(:meta_refresh) { @refresh_interval&.to_s || '15' } %> +<% self.title = t('titles.doc_auth.verify') %> +<% content_for(:pre_flash_content) do %> + <%= render StepIndicatorComponent.new( + steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS, + current_step: :verify_id, + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + ) %> +<% end %> + +<%= image_tag( + asset_url('id-card.svg'), + alt: '', + width: 216, + height: 116, + class: 'margin-bottom-4', + ) %> + +<%= render PageHeadingComponent.new do %> + <%= t('doc_auth.headings.interstitial') %> +<% end %> + +

+ <%= t('doc_auth.info.interstitial_eta') %> +

+ +

+ <%= t('doc_auth.info.interstitial_thanks') %> +

diff --git a/config/application.yml.default b/config/application.yml.default index 2dbc7fbe7f3..ad74721128a 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -106,6 +106,8 @@ doc_auth_max_attempts: 5 doc_auth_max_capture_attempts_before_native_camera: 3 doc_auth_max_submission_attempts_before_native_camera: 3 doc_auth_selfie_desktop_test_mode: false +doc_auth_socure_wait_polling_refresh_max_seconds: 15 +doc_auth_socure_wait_polling_timeout_minutes: 2 doc_auth_supported_country_codes: '["US", "GU", "VI", "AS", "MP", "PR", "USA" ,"GUM", "VIR", "ASM", "MNP", "PRI"]' doc_auth_vendor: 'mock' doc_auth_vendor_default: 'mock' diff --git a/lib/identity_config.rb b/lib/identity_config.rb index ff40a55b211..3de48d0a232 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -124,6 +124,8 @@ def self.store config.add(:doc_auth_max_capture_attempts_before_native_camera, type: :integer) config.add(:doc_auth_max_submission_attempts_before_native_camera, type: :integer) config.add(:doc_auth_selfie_desktop_test_mode, type: :boolean) + config.add(:doc_auth_socure_wait_polling_refresh_max_seconds, type: :integer) + config.add(:doc_auth_socure_wait_polling_timeout_minutes, type: :integer) config.add(:doc_auth_supported_country_codes, type: :json) config.add(:doc_auth_vendor, type: :string) config.add(:doc_auth_vendor_default, type: :string) diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 7dca6ffd863..88d1ca61523 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -388,19 +388,6 @@ end end - context 'user has an establishing in-person enrollment' do - let!(:enrollment) { create(:in_person_enrollment, :establishing, user: user, profile: nil) } - - it 'cancels the establishing enrollment' do - expect(user.establishing_in_person_enrollment).to eq enrollment - - put :update - - expect(enrollment.reload.cancelled?).to eq(true) - expect(user.reload.establishing_in_person_enrollment).to be_nil - end - end - context 'ocr confirmation pending' do before do subject.document_capture_session.ocr_confirmation_pending = true diff --git a/spec/controllers/idv/socure/document_capture_controller_spec.rb b/spec/controllers/idv/socure/document_capture_controller_spec.rb index ddb4542e05b..f0fc04b39e0 100644 --- a/spec/controllers/idv/socure/document_capture_controller_spec.rb +++ b/spec/controllers/idv/socure/document_capture_controller_spec.rb @@ -41,6 +41,8 @@ allow(subject).to receive(:user_session).and_return(user_session) subject.idv_session.document_capture_session_uuid = document_capture_session.uuid + + stub_analytics end describe '#step_info' do @@ -274,6 +276,7 @@ get(:update) expect(response).to redirect_to(idv_ssn_path) + expect(@analytics).to have_logged_event('IdV: doc auth document_capture submitted') end context 'when doc auth fails' do @@ -283,6 +286,29 @@ get(:update) expect(response).to redirect_to(idv_socure_document_capture_path) + expect(@analytics).to have_logged_event('IdV: doc auth document_capture submitted') + end + end + + context 'when stored_result is nil' do + let(:stored_result) { nil } + + it 'renders the wait view' do + get(:update) + expect(response).to render_template('idv/socure/document_capture/wait') + expect(@analytics).to have_logged_event(:idv_doc_auth_document_capture_polling_wait_visited) + end + + context 'when the wait times out' do + before do + allow(subject).to receive(:wait_timed_out?).and_return(true) + end + + it 'renders a technical difficulties message' do + get(:update) + expect(response).to have_http_status(:ok) + expect(response.body).to eq('Technical difficulties!!!') + end end end From 78da74951b7c7c2ccb3afdcf0f1b366db665021d Mon Sep 17 00:00:00 2001 From: Shane Chesnutt Date: Tue, 19 Nov 2024 12:30:58 -0500 Subject: [PATCH 06/13] LG-12269 Improve ID-IPP feature test structure (#11506) [skip changelog] --- .../idv/steps/in_person/address_spec.rb | 10 +- ...id_controller_spec.rb => state_id_spec.rb} | 274 +++++----- .../idv/steps/in_person/state_id_step_spec.rb | 471 ------------------ .../idv/steps/in_person/verify_info_spec.rb | 281 +++++------ spec/support/features/in_person_helper.rb | 9 + 5 files changed, 266 insertions(+), 779 deletions(-) rename spec/features/idv/steps/in_person/{state_id_controller_spec.rb => state_id_spec.rb} (62%) delete mode 100644 spec/features/idv/steps/in_person/state_id_step_spec.rb diff --git a/spec/features/idv/steps/in_person/address_spec.rb b/spec/features/idv/steps/in_person/address_spec.rb index a3bb3a03ae3..42d5cbabb04 100644 --- a/spec/features/idv/steps/in_person/address_spec.rb +++ b/spec/features/idv/steps/in_person/address_spec.rb @@ -51,7 +51,6 @@ 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_url) expect(page).to have_text(InPersonHelper::GOOD_ADDRESS1) - expect(page).to have_text(InPersonHelper::GOOD_ADDRESS2) expect(page).to have_text(InPersonHelper::GOOD_CITY) expect(page).to have_text(InPersonHelper::GOOD_ZIPCODE) expect(page).to have_text(Idp::Constants::MOCK_IDV_APPLICANT_STATE) @@ -72,7 +71,6 @@ # address page has fields that are pre-populated expect(page).to have_content(t('in_person_proofing.headings.update_address')) expect(page).to have_field(t('idv.form.address1'), with: InPersonHelper::GOOD_ADDRESS1) - expect(page).to have_field(t('idv.form.address2'), with: InPersonHelper::GOOD_ADDRESS2) expect(page).to have_field(t('idv.form.city'), with: InPersonHelper::GOOD_CITY) expect(page).to have_field(t('idv.form.zipcode'), with: InPersonHelper::GOOD_ZIPCODE) expect(page).to have_field( @@ -82,8 +80,8 @@ end end - context 'Transliterable Validation' do - before(:each) do + context 'transliterable Validation' do + before do allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled). and_return(true) end @@ -134,7 +132,7 @@ end end - context 'Validation' do + context 'validation' do it 'validates zip code input', allow_browser_log: true do complete_idv_steps_before_address @@ -156,7 +154,7 @@ end end - context 'State selection' do + context 'state selection' do it 'shows address hint when user selects state that has a specific hint', allow_browser_log: true do complete_idv_steps_before_address diff --git a/spec/features/idv/steps/in_person/state_id_controller_spec.rb b/spec/features/idv/steps/in_person/state_id_spec.rb similarity index 62% rename from spec/features/idv/steps/in_person/state_id_controller_spec.rb rename to spec/features/idv/steps/in_person/state_id_spec.rb index 1bf4c26f9a7..b9ae0d07f62 100644 --- a/spec/features/idv/steps/in_person/state_id_controller_spec.rb +++ b/spec/features/idv/steps/in_person/state_id_spec.rb @@ -14,11 +14,7 @@ expect(page).to have_content(t('forms.buttons.continue')) expect(page).to have_content( - strip_nbsp( - t( - 'in_person_proofing.headings.state_id_milestone_2', - ), - ), + strip_nbsp(t('in_person_proofing.headings.state_id_milestone_2')), ) end @@ -122,157 +118,145 @@ ) end - context 'same address as id', - allow_browser_log: true do + context 'same_address_as_id', allow_browser_log: true do let(:user) { user_with_2fa } - before(:each) do + before do sign_in_and_2fa_user(user) begin_in_person_proofing(user) complete_prepare_step(user) complete_location_step(user) end - it 'does not update their previous selection of "Yes, - I live at the address on my state-issued ID"' do - complete_state_id_controller(user, same_address_as_id: true) - # skip address step - complete_ssn_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_content(t('headings.verify')) - # expect to see state ID address update on verify twice - expect(page).to have_text('test update address').twice # for state id addr and addr update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # expect "Yes, I live at a different address" is checked" - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_yes'), - visible: false, - ) - end - - it 'does not update their previous selection of "No, I live at a different address"' do - complete_state_id_controller(user, same_address_as_id: false) - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - complete_ssn_step(user) - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_content(t('headings.verify')) - # expect to see state ID address update on verify - expect(page).to have_text('test update address').once # only state id address update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_no'), - visible: false, - ) - end - - it 'updates their previous selection from "Yes" TO "No, I live at a different address"' do - complete_state_id_controller(user, same_address_as_id: true) - # skip address step - complete_ssn_step(user) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - # change response to No - choose t('in_person_proofing.form.state_id.same_address_as_id_no') - click_button t('forms.buttons.submit.update') - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # expect to see state ID address update on verify - expect(page).to have_text('test update address').once # only state id address update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # check that the "No, I live at a different address" is checked" - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_no'), - visible: false, - ) + context 'when the user answers "Yes" for same address as state ID' do + before do + complete_state_id_controller(user, same_address_as_id: true) + complete_ssn_step(user) + click_link t('idv.buttons.change_state_id_label') + end + + context 'when the user does not update the same address value' do + it 'does not change their previous selection' do + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # change address + fill_in t('in_person_proofing.form.state_id.address1'), with: '' + fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' + click_button t('forms.buttons.submit.update') + # expect to be back on verify page + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_content(t('headings.verify')) + # expect to see state ID address update on verify twice + # for state id address and address update + expect(page).to have_text('test update address').twice + # click update state id address + click_link t('idv.buttons.change_state_id_label') + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # expect "Yes, I live at a different address" is checked" + expect(page).to have_checked_field( + t('in_person_proofing.form.state_id.same_address_as_id_yes'), + visible: false, + ) + end + end + + context 'when the user updates the same address value to "No"' do + it 'updates their selection to "No"' do + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # change address + fill_in t('in_person_proofing.form.state_id.address1'), with: '' + fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' + # change response to No + choose t('in_person_proofing.form.state_id.same_address_as_id_no') + click_button t('forms.buttons.submit.update') + # expect to be on address page + expect(page).to have_content(t('in_person_proofing.headings.address')) + # complete address step + complete_address_step(user) + # expect to be on verify page + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + # expect to see state ID address update on verify + expect(page).to have_text('test update address').once # only state id address update + # click update state id address + click_link t('idv.buttons.change_state_id_label') + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # check that the "No, I live at a different address" is checked" + expect(page).to have_checked_field( + t('in_person_proofing.form.state_id.same_address_as_id_no'), + visible: false, + ) + end + end end - it 'updates their previous selection from "No" TO "Yes, - I live at the address on my state-issued ID"' do - complete_state_id_controller(user, same_address_as_id: false) - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - complete_ssn_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - # change response to Yes - choose t('in_person_proofing.form.state_id.same_address_as_id_yes') - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # expect to see state ID address update on verify twice - expect(page).to have_text('test update address').twice # for state id addr and addr update - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_yes'), - visible: false, - ) + context 'when the user answers "No" for same address as state ID' do + before do + complete_state_id_controller(user, same_address_as_id: false) + complete_address_step(user) + complete_ssn_step(user) + click_link t('idv.buttons.change_state_id_label') + end + + context 'when the user does not update the same address value' do + it 'does not change their previous selection' do + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # change address + fill_in t('in_person_proofing.form.state_id.address1'), with: '' + fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' + click_button t('forms.buttons.submit.update') + # expect to be back on verify page + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_content(t('headings.verify')) + # expect to see state ID address update on verify + expect(page).to have_text('test update address').once # only state id address update + # click update state id address + click_link t('idv.buttons.change_state_id_label') + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + expect(page).to have_checked_field( + t('in_person_proofing.form.state_id.same_address_as_id_no'), + visible: false, + ) + end + end + + context 'when the user updates the same address value to "Yes"' do + it 'updates their selection to "Yes"' do + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + # change address + fill_in t('in_person_proofing.form.state_id.address1'), with: '' + fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' + # change response to Yes + choose t('in_person_proofing.form.state_id.same_address_as_id_yes') + click_button t('forms.buttons.submit.update') + # expect to be back on verify page + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + # expect to see state ID address update on verify twice + # for state id address and address update + expect(page).to have_text('test update address').twice + # click update state ID button on the verify page + click_link t('idv.buttons.change_state_id_label') + # expect to be on the state ID page + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + expect(page).to have_checked_field( + t('in_person_proofing.form.state_id.same_address_as_id_yes'), + visible: false, + ) + end + end end end end - context 'Validation' do + context 'validation' do it 'validates zip code input', allow_browser_log: true do complete_steps_before_state_id_controller @@ -325,8 +309,8 @@ end end - context 'Transliterable Validation' do - before(:each) do + context 'transliterable validation' do + before do allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled). and_return(true) end @@ -394,7 +378,7 @@ end end - context 'State selection' do + context 'state selection' do it 'shows address hint when user selects state that has a specific hint', allow_browser_log: true do complete_steps_before_state_id_controller diff --git a/spec/features/idv/steps/in_person/state_id_step_spec.rb b/spec/features/idv/steps/in_person/state_id_step_spec.rb deleted file mode 100644 index a4496cf623a..00000000000 --- a/spec/features/idv/steps/in_person/state_id_step_spec.rb +++ /dev/null @@ -1,471 +0,0 @@ -require 'rails_helper' - -RSpec.describe 'doc auth IPP state ID step', js: true do - include IdvStepHelper - include InPersonHelper - - before do - allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true) - end - - context 'when visiting state id for the first time' do - it 'displays correct heading and button text', allow_browser_log: true do - complete_steps_before_state_id_step - - expect(page).to have_content(t('forms.buttons.continue')) - expect(page).to have_content( - strip_nbsp( - t( - 'in_person_proofing.headings.state_id_milestone_2', - ), - ), - ) - end - - it 'allows the user to cancel and start over', allow_browser_log: true do - complete_steps_before_state_id_step - - expect(page).not_to have_content('forms.buttons.back') - - click_link t('links.cancel') - click_on t('idv.cancel.actions.start_over') - expect(page).to have_current_path(idv_welcome_path) - end - - it 'allows the user to cancel and return', allow_browser_log: true do - complete_steps_before_state_id_step - - expect(page).not_to have_content('forms.buttons.back') - - click_link t('links.cancel') - click_on t('idv.cancel.actions.keep_going') - expect(page).to have_current_path(idv_in_person_state_id_path, wait: 10) - end - - it 'allows user to submit valid inputs on form', allow_browser_log: true do - complete_steps_before_state_id_step - fill_out_state_id_form_ok(same_address_as_id: true) - click_idv_continue - - expect(page).to have_current_path(idv_in_person_ssn_url, wait: 10) - complete_ssn_step - - 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_url) - 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(InPersonHelper::GOOD_STATE_ID_NUMBER) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE) - end - end - - context 'updating state id page' do - it 'has form fields that are pre-populated', allow_browser_log: true do - complete_steps_before_state_id_step - - fill_out_state_id_form_ok(same_address_as_id: true) - click_idv_continue - expect(page).to have_current_path(idv_in_person_ssn_url, wait: 10) - complete_ssn_step - expect(page).to have_current_path(idv_in_person_verify_info_path, wait: 10) - click_link t('idv.buttons.change_state_id_label') - - # state id page has fields that are pre-populated - expect(page).to have_current_path(idv_in_person_state_id_path, wait: 10) - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_field( - t('in_person_proofing.form.state_id.first_name'), - with: InPersonHelper::GOOD_FIRST_NAME, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.last_name'), - with: InPersonHelper::GOOD_LAST_NAME, - ) - expect(page).to have_field(t('components.memorable_date.month'), with: '10') - expect(page).to have_field(t('components.memorable_date.day'), with: '6') - expect(page).to have_field(t('components.memorable_date.year'), with: '1938') - expect(page).to have_field( - t('in_person_proofing.form.state_id.state_id_jurisdiction'), - with: Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.state_id_number'), - with: InPersonHelper::GOOD_STATE_ID_NUMBER, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.address1'), - with: InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.address2'), - with: InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.city'), - with: InPersonHelper::GOOD_IDENTITY_DOC_CITY, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.zipcode'), - with: InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE, - ) - expect(page).to have_field( - t('in_person_proofing.form.state_id.identity_doc_address_state'), - with: Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], - ) - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_yes'), - visible: false, - ) - end - - context 'same address as id', - allow_browser_log: true do - let(:user) { user_with_2fa } - - before(:each) do - sign_in_and_2fa_user(user) - begin_in_person_proofing(user) - complete_prepare_step(user) - complete_location_step(user) - end - - it 'does not update their previous selection of "Yes, - I live at the address on my state-issued ID"' do - complete_state_id_step(user, same_address_as_id: true) - # skip address step - complete_ssn_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_content(t('headings.verify')) - # expect to see state ID address update on verify twice - expect(page).to have_text('test update address').twice # for state id addr and addr update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # expect "Yes, I live at a different address" is checked" - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_yes'), - visible: false, - ) - end - - it 'does not update their previous selection of "No, I live at a different address"' do - complete_state_id_step(user, same_address_as_id: false) - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - complete_ssn_step(user) - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_content(t('headings.verify')) - # expect to see state ID address update on verify - expect(page).to have_text('test update address').once # only state id address update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_no'), - visible: false, - ) - end - - it 'updates their previous selection from "Yes" TO "No, I live at a different address"' do - complete_state_id_step(user, same_address_as_id: true) - # skip address step - complete_ssn_step(user) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - # change response to No - choose t('in_person_proofing.form.state_id.same_address_as_id_no') - click_button t('forms.buttons.submit.update') - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # expect to see state ID address update on verify - expect(page).to have_text('test update address').once # only state id address update - # click update state id address - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # check that the "No, I live at a different address" is checked" - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_no'), - visible: false, - ) - end - - it 'updates their previous selection from "No" TO "Yes, - I live at the address on my state-issued ID"' do - complete_state_id_step(user, same_address_as_id: false) - # expect to be on address page - expect(page).to have_content(t('in_person_proofing.headings.address')) - # complete address step - complete_address_step(user) - complete_ssn_step(user) - # expect to be on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - # change address - fill_in t('in_person_proofing.form.state_id.address1'), with: '' - fill_in t('in_person_proofing.form.state_id.address1'), with: 'test update address' - # change response to Yes - choose t('in_person_proofing.form.state_id.same_address_as_id_yes') - click_button t('forms.buttons.submit.update') - # expect to be back on verify page - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - # expect to see state ID address update on verify twice - expect(page).to have_text('test update address').twice # for state id addr and addr update - # click update state ID button on the verify page - click_link t('idv.buttons.change_state_id_label') - # expect to be on the state ID page - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_checked_field( - t('in_person_proofing.form.state_id.same_address_as_id_yes'), - visible: false, - ) - end - end - end - - context 'Validation' do - it 'validates zip code input', allow_browser_log: true do - complete_steps_before_state_id_step - - fill_out_state_id_form_ok(same_address_as_id: true) - fill_in t('in_person_proofing.form.state_id.zipcode'), with: '' - fill_in t('in_person_proofing.form.state_id.zipcode'), with: 'invalid input' - expect(page).to have_field(t('in_person_proofing.form.state_id.zipcode'), with: '') - - # enter valid characters, but invalid length - fill_in t('in_person_proofing.form.state_id.zipcode'), with: '123' - click_idv_continue - expect(page).to have_css( - '.usa-error-message', - text: t('idv.errors.pattern_mismatch.zipcode'), - ) - - # enter a valid zip and make sure we can continue - fill_in t('in_person_proofing.form.state_id.zipcode'), with: '123456789' - expect(page).to have_field( - t('in_person_proofing.form.state_id.zipcode'), - with: '12345-6789', - ) - click_idv_continue - expect(page).to have_current_path(idv_in_person_ssn_url) - end - - it 'shows error for dob under minimum age', allow_browser_log: true do - complete_steps_before_state_id_step - - fill_in t('components.memorable_date.month'), with: '1' - fill_in t('components.memorable_date.day'), with: '1' - fill_in t('components.memorable_date.year'), with: Time.zone.now.strftime('%Y') - click_idv_continue - expect(page).to have_content( - t( - 'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age', - app_name: APP_NAME, - ), - ) - - year = (Time.zone.now - 13.years).strftime('%Y') - fill_in t('components.memorable_date.year'), with: year - click_idv_continue - expect(page).not_to have_content( - t( - 'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age', - app_name: APP_NAME, - ), - ) - end - end - - context 'Transliterable Validation' do - before(:each) do - allow(IdentityConfig.store).to receive(:usps_ipp_transliteration_enabled). - and_return(true) - end - - it 'shows validation errors', - allow_browser_log: true do - complete_steps_before_state_id_step - - fill_out_state_id_form_ok - fill_in t('in_person_proofing.form.state_id.first_name'), with: 'T0mmy "Lee"' - fill_in t('in_person_proofing.form.state_id.last_name'), with: 'Джейкоб' - fill_in t('in_person_proofing.form.state_id.address1'), with: '#1 $treet' - fill_in t('in_person_proofing.form.state_id.address2'), with: 'Gr@nd Lañe^' - fill_in t('in_person_proofing.form.state_id.city'), with: 'N3w C!ty' - click_idv_continue - - expect(page).to have_content( - I18n.t( - 'in_person_proofing.form.state_id.errors.unsupported_chars', - char_list: '", 0', - ), - ) - - expect(page).to have_content( - I18n.t( - 'in_person_proofing.form.state_id.errors.unsupported_chars', - char_list: 'Д, б, е, ж, й, к, о', - ), - ) - - expect(page).to have_content( - I18n.t( - 'in_person_proofing.form.state_id.errors.unsupported_chars', - char_list: '$', - ), - ) - - expect(page).to have_content( - I18n.t( - 'in_person_proofing.form.state_id.errors.unsupported_chars', - char_list: '@, ^', - ), - ) - - expect(page).to have_content( - I18n.t( - 'in_person_proofing.form.state_id.errors.unsupported_chars', - char_list: '!, 3', - ), - ) - - fill_in t('in_person_proofing.form.state_id.first_name'), - with: InPersonHelper::GOOD_FIRST_NAME - fill_in t('in_person_proofing.form.state_id.last_name'), - with: InPersonHelper::GOOD_LAST_NAME - fill_in t('in_person_proofing.form.state_id.address1'), - with: InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1 - fill_in t('in_person_proofing.form.state_id.address2'), - with: InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2 - fill_in t('in_person_proofing.form.state_id.city'), - with: InPersonHelper::GOOD_IDENTITY_DOC_CITY - click_idv_continue - - expect(page).to have_current_path(idv_in_person_address_url, wait: 10) - end - end - - context 'State selection' do - it 'shows address hint when user selects state that has a specific hint', - allow_browser_log: true do - complete_steps_before_state_id_step - - # state id page - select 'Puerto Rico', - from: t('in_person_proofing.form.state_id.identity_doc_address_state') - - expect(page).to have_content(I18n.t('in_person_proofing.form.state_id.address1_hint')) - expect(page).to have_content(I18n.t('in_person_proofing.form.state_id.address2_hint')) - - # change state selection - fill_out_state_id_form_ok(same_address_as_id: true) - expect(page).not_to have_content(I18n.t('in_person_proofing.form.state_id.address1_hint')) - expect(page).not_to have_content(I18n.t('in_person_proofing.form.state_id.address2_hint')) - - # re-select puerto rico - select 'Puerto Rico', - from: t('in_person_proofing.form.state_id.identity_doc_address_state') - click_idv_continue - - # ssn page - expect(page).to have_current_path(idv_in_person_ssn_url) - complete_ssn_step - - # verify page - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text('PR') - - # update state ID - click_link t('idv.buttons.change_state_id_label') - - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - expect(page).to have_content(I18n.t('in_person_proofing.form.state_id.address1_hint')) - expect(page).to have_content(I18n.t('in_person_proofing.form.state_id.address2_hint')) - end - - it 'shows id number hint when user selects issuing state that has a specific hint', - allow_browser_log: true do - complete_steps_before_state_id_step - - # expect default hint to be present - expect(page).to have_content(t('in_person_proofing.form.state_id.state_id_number_hint')) - - select 'Texas', - from: t('in_person_proofing.form.state_id.state_id_jurisdiction') - expect(page).to have_content(t('in_person_proofing.form.state_id.state_id_number_texas_hint')) - expect(page).not_to have_content(t('in_person_proofing.form.state_id.state_id_number_hint')) - - select 'Florida', - from: t('in_person_proofing.form.state_id.state_id_jurisdiction') - expect(page).not_to have_content( - t('in_person_proofing.form.state_id.state_id_number_texas_hint'), - ) - expect(page).not_to have_content(t('in_person_proofing.form.state_id.state_id_number_hint')) - expect(page).to have_content strip_tags( - t('in_person_proofing.form.state_id.state_id_number_florida_hint_html').gsub( - / /, ' ' - ), - ) - - # select a state without a state specific hint - select 'Ohio', - from: t('in_person_proofing.form.state_id.state_id_jurisdiction') - expect(page).to have_content(t('in_person_proofing.form.state_id.state_id_number_hint')) - expect(page).not_to have_content( - t('in_person_proofing.form.state_id.state_id_number_texas_hint'), - ) - expect(page).not_to have_content( - t('in_person_proofing.form.state_id.state_id_number_florida_hint_html'), - ) - 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 84bd305ce6e..528e6607973 100644 --- a/spec/features/idv/steps/in_person/verify_info_spec.rb +++ b/spec/features/idv/steps/in_person/verify_info_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' require 'axe-rspec' -RSpec.describe 'doc auth IPP VerifyInfo', js: true do +RSpec.describe 'doc auth IPP VerifyInfo', js: true, allow_browser_log: true do include IdvStepHelper include InPersonHelper @@ -16,165 +16,132 @@ and_return(enrollment) end - it 'provides back buttons for address, state ID, and SSN that discard changes', - allow_browser_log: true do - sign_in_and_2fa_user(user) - begin_in_person_proofing(user) - complete_prepare_step(user) - complete_location_step(user) - complete_state_id_controller(user) - 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) - expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME) - expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT) - expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice - expect(page).to have_text( - Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], - count: 3, - ) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice - expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) - - # click update state ID button - click_link t('idv.buttons.change_state_id_label') - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - fill_in t('in_person_proofing.form.state_id.first_name'), with: 'bad first name' - click_doc_auth_back_link - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) - expect(page).not_to have_text('bad first name') - - # click update address link - click_link t('idv.buttons.change_address_label') - expect(page).to have_content(t('in_person_proofing.headings.update_address')) - fill_in t('idv.form.address1'), with: 'bad address' - click_doc_auth_back_link - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1) - expect(page).not_to have_text('bad address') - - # click update ssn button - click_on t('idv.buttons.change_ssn_label') - expect(page).to have_content(t('doc_auth.headings.ssn_update')) - fill_out_ssn_form_fail - click_doc_auth_back_link - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) - - complete_verify_step(user) - - # phone page - expect(page).to have_content(t('doc_auth.forms.doc_success')) - expect(page).to have_content(t('titles.idv.phone')) + context 'when visiting verify info for the first time' do + before do + complete_steps_before_info_verify(user) + end + + it 'displays all info submitted in the address, state ID and SSN steps' do + 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) + expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT) + expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice + expect(page).to have_text( + Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], + count: 3, + ) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + end end - it 'returns the user to the verify info page when updates are made', - allow_browser_log: true do - sign_in_and_2fa_user(user) - begin_in_person_proofing(user) - complete_prepare_step(user) - complete_location_step(user) - complete_state_id_controller(user) - 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_current_path(idv_in_person_verify_info_path) - 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(InPersonHelper::GOOD_STATE_ID_NUMBER) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice - expect(page).to have_text( - Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], - count: 3, - ) - expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice - expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) - - # click update state ID button - click_link t('idv.buttons.change_state_id_label') - expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) - fill_in t('in_person_proofing.form.state_id.first_name'), with: 'Natalya' - click_button t('forms.buttons.submit.update') - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text('Natalya') - expect(page).not_to have_text('bad first name') - - # click update address link - click_link t('idv.buttons.change_address_label') - expect(page).to have_content(t('in_person_proofing.headings.update_address')) - fill_in t('idv.form.address1'), with: '987 Fake St.' - click_button t('forms.buttons.submit.update') - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text('987 Fake St.') - expect(page).not_to have_text('bad address') - - # click update ssn button - click_on t('idv.buttons.change_ssn_label') - expect(page).to have_content(t('doc_auth.headings.ssn_update')) - fill_in t('idv.form.ssn_label'), with: '900-12-2345' - click_button t('forms.buttons.submit.update') - expect(page).to have_content(t('headings.verify')) - expect(page).to have_current_path(idv_in_person_verify_info_path) - expect(page).to have_text('9**-**-***5') - - complete_verify_step(user) - - # phone page - expect(page).to have_content(t('doc_auth.forms.doc_success')) - expect(page).to have_content(t('titles.idv.phone')) + context 'when performing updates for the address, state ID, and SSN info' do + before do + complete_steps_before_info_verify(user) + end + + it 'provides a back button for discarding changes' do + # click update state ID button + click_link t('idv.buttons.change_state_id_label') + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + fill_in t('in_person_proofing.form.state_id.first_name'), with: 'bad first name' + click_doc_auth_back_link + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) + expect(page).not_to have_text('bad first name') + + # click update address link + click_link t('idv.buttons.change_address_label') + expect(page).to have_content(t('in_person_proofing.headings.update_address')) + fill_in t('idv.form.address1'), with: 'bad address' + click_doc_auth_back_link + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1) + expect(page).not_to have_text('bad address') + + # click update ssn button + click_on t('idv.buttons.change_ssn_label') + expect(page).to have_content(t('doc_auth.headings.ssn_update')) + fill_out_ssn_form_fail + click_doc_auth_back_link + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + end + + it 'updates the info on the verify info page' do + # click update state ID button + click_link t('idv.buttons.change_state_id_label') + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + fill_in t('in_person_proofing.form.state_id.first_name'), with: 'Natalya' + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text('Natalya') + expect(page).not_to have_text('bad first name') + + # click update address link + click_link t('idv.buttons.change_address_label') + expect(page).to have_content(t('in_person_proofing.headings.update_address')) + fill_in t('idv.form.address1'), with: '987 Fake St.' + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text('987 Fake St.') + expect(page).not_to have_text('bad address') + + # click update ssn button + click_on t('idv.buttons.change_ssn_label') + expect(page).to have_content(t('doc_auth.headings.ssn_update')) + fill_in t('idv.form.ssn_label'), with: '900-12-2345' + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text('9**-**-***5') + end end - it 'does not proceed to the next page if resolution fails', - allow_browser_log: true do - sign_in_and_2fa_user - - begin_in_person_proofing(user) - complete_prepare_step(user) - complete_location_step(user) - complete_state_id_controller(user) - fill_out_ssn_form_with_ssn_that_fails_resolution - click_idv_continue - complete_verify_step(user) - - expect(page).to have_current_path(idv_session_errors_warning_path(flow: 'in_person')) - click_on t('idv.failure.button.warning') - - expect(page).to have_current_path(idv_in_person_verify_info_path) - end - - it 'proceeds to the next page if resolution passes', - allow_browser_log: true do - sign_in_and_2fa_user - begin_in_person_proofing(user) - complete_prepare_step(user) - complete_location_step(user) - complete_state_id_controller(user) - complete_ssn_step(user) - complete_verify_step(user) - - expect(page).to have_content(t('titles.idv.phone')) - expect(fake_analytics).to have_logged_event( - 'IdV: doc auth verify proofing results', - hash_including(analytics_id: 'In Person Proofing'), - ) + context 'when completing the verify info step' do + context 'when SSN resolution fails' do + it 'navigates to the session warning page' do + sign_in_and_2fa_user(user) + begin_in_person_proofing(user) + complete_prepare_step(user) + complete_location_step(user) + complete_state_id_controller(user) + fill_out_ssn_form_with_ssn_that_fails_resolution + click_idv_continue + complete_verify_step(user) + + expect(page).to have_current_path(idv_session_errors_warning_path(flow: 'in_person')) + click_on t('idv.failure.button.warning') + + expect(page).to have_current_path(idv_in_person_verify_info_path) + end + end + + context 'when SSN resolution passes' do + before do + complete_steps_before_info_verify(user) + complete_verify_step(user) + end + + it 'navigates to the verify phone' do + expect(page).to have_content(t('doc_auth.forms.doc_success')) + expect(page).to have_content(t('titles.idv.phone')) + expect(fake_analytics).to have_logged_event( + 'IdV: doc auth verify proofing results', + hash_including(analytics_id: 'In Person Proofing'), + ) + end + end end end diff --git a/spec/support/features/in_person_helper.rb b/spec/support/features/in_person_helper.rb index 36730c23cc3..41fbaef6b9c 100644 --- a/spec/support/features/in_person_helper.rb +++ b/spec/support/features/in_person_helper.rb @@ -174,6 +174,15 @@ def complete_steps_before_state_id_controller expect(page).to have_current_path(idv_in_person_state_id_path, wait: 10) end + def complete_steps_before_info_verify(user = user_with_2fa) + sign_in_and_2fa_user(user) + begin_in_person_proofing(user) + complete_prepare_step(user) + complete_location_step(user) + complete_state_id_controller(user) + complete_ssn_step(user) + end + def complete_all_in_person_proofing_steps(user = user_with_2fa, tmx_status = nil, same_address_as_id: true) complete_prepare_step(user) From cb7856179082336539025fc7f652b5b2a87cdfc9 Mon Sep 17 00:00:00 2001 From: Matt Hinz Date: Tue, 19 Nov 2024 10:46:57 -0800 Subject: [PATCH 07/13] LG-15090: Update Socure config key names (#11512) * Change socure_enabled config to socure_docv_enabled Reduce potential confusion between doc auth and identity resolution Socure implementations [skip changelog] * socure_document_request_endpoint -> socure_docv_document_request_endpoint Maybe a little redundant, but it's (somewhat) consistent? * Remove socure_standard_capture_desktop_enabled Does not appear to be referenced anywhere * Remove socure_webhook_enabled does not appear to be referenced * socure_webhook_secret_key_queue -> socure_docv_webhook_secret_key_queue * socure_webhook_secret_key -> socure_docv_webhook_secret_key * let's get it sorted in here * normalize_yaml yaml is good --- .../socure/document_capture_controller.rb | 2 +- .../idv/socure/document_capture_controller.rb | 2 +- app/controllers/socure_webhook_controller.rb | 6 +++--- .../doc_auth/socure/requests/document_request.rb | 2 +- config/application.yml.default | 14 ++++++-------- lib/identity_config.rb | 10 ++++------ .../socure/document_capture_controller_spec.rb | 14 +++++++------- .../idv/socure/document_capture_controller_spec.rb | 14 +++++++------- spec/controllers/socure_webhook_controller_spec.rb | 12 ++++++------ spec/features/idv/doc_auth/hybrid_handoff_spec.rb | 6 +++--- spec/features/idv/hybrid_mobile/entry_spec.rb | 2 +- .../idv/hybrid_mobile/hybrid_mobile_spec.rb | 2 +- spec/services/doc_auth/socure/request_spec.rb | 2 +- .../socure/requests/document_request_spec.rb | 2 +- 14 files changed, 43 insertions(+), 47 deletions(-) diff --git a/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb index c62aab7201f..6dfd7980241 100644 --- a/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb @@ -10,7 +10,7 @@ class DocumentCaptureController < ApplicationController include RenderConditionConcern include DocumentCaptureConcern - check_or_render_not_found -> { IdentityConfig.store.socure_enabled } + check_or_render_not_found -> { IdentityConfig.store.socure_docv_enabled } before_action :check_valid_document_capture_session, except: [:update] before_action -> { redirect_to_correct_vendor(Idp::Constants::Vendors::SOCURE, true) } diff --git a/app/controllers/idv/socure/document_capture_controller.rb b/app/controllers/idv/socure/document_capture_controller.rb index 8a67b633466..b93ec65334c 100644 --- a/app/controllers/idv/socure/document_capture_controller.rb +++ b/app/controllers/idv/socure/document_capture_controller.rb @@ -8,7 +8,7 @@ class DocumentCaptureController < ApplicationController include DocumentCaptureConcern include RenderConditionConcern - check_or_render_not_found -> { IdentityConfig.store.socure_enabled } + check_or_render_not_found -> { IdentityConfig.store.socure_docv_enabled } before_action :confirm_not_rate_limited before_action :confirm_step_allowed before_action -> { redirect_to_correct_vendor(Idp::Constants::Vendors::SOCURE, false) } diff --git a/app/controllers/socure_webhook_controller.rb b/app/controllers/socure_webhook_controller.rb index b30b14a6306..0d0354da7d1 100644 --- a/app/controllers/socure_webhook_controller.rb +++ b/app/controllers/socure_webhook_controller.rb @@ -4,7 +4,7 @@ class SocureWebhookController < ApplicationController include RenderConditionConcern skip_before_action :verify_authenticity_token - check_or_render_not_found -> { IdentityConfig.store.socure_enabled } + check_or_render_not_found -> { IdentityConfig.store.socure_docv_enabled } before_action :check_token before_action :check_socure_event @@ -65,12 +65,12 @@ def token_valid? def verify_current_key(authorization_header:) ActiveSupport::SecurityUtils.secure_compare( authorization_header, - IdentityConfig.store.socure_webhook_secret_key, + IdentityConfig.store.socure_docv_webhook_secret_key, ) end def verify_queue(authorization_header:) - IdentityConfig.store.socure_webhook_secret_key_queue.any? do |key| + IdentityConfig.store.socure_docv_webhook_secret_key_queue.any? do |key| ActiveSupport::SecurityUtils.secure_compare( authorization_header, key, diff --git a/app/services/doc_auth/socure/requests/document_request.rb b/app/services/doc_auth/socure/requests/document_request.rb index 5121e72c577..f214f8867c6 100644 --- a/app/services/doc_auth/socure/requests/document_request.rb +++ b/app/services/doc_auth/socure/requests/document_request.rb @@ -63,7 +63,7 @@ def method end def endpoint - IdentityConfig.store.socure_document_request_endpoint + IdentityConfig.store.socure_docv_document_request_endpoint end def metric_name diff --git a/config/application.yml.default b/config/application.yml.default index ad74721128a..a69b70d25ea 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -381,18 +381,16 @@ sign_in_user_id_per_ip_attempt_window_in_minutes: 720 sign_in_user_id_per_ip_attempt_window_max_minutes: 43_200 sign_in_user_id_per_ip_max_attempts: 50 skip_encryption_allowed_list: '["urn:gov:gsa:SAML:2.0.profiles:sp:sso:dev", "urn:gov:gsa:SAML:2.0.profiles:sp:sso:int"]' -socure_document_request_endpoint: '' -socure_enabled: false +socure_docv_document_request_endpoint: '' +socure_docv_enabled: false +socure_docv_webhook_secret_key: '' +socure_docv_webhook_secret_key_queue: '[]' socure_idplus_api_key: '' socure_idplus_base_url: '' socure_idplus_timeout_in_seconds: 5 socure_reason_code_api_key: '' socure_reason_code_base_url: '' socure_reason_code_timeout_in_seconds: 5 -socure_standard_capture_desktop_enabled: false -socure_webhook_enabled: false -socure_webhook_secret_key: '' -socure_webhook_secret_key_queue: '[]' sp_handoff_bounce_max_seconds: 2 sp_issuer_user_counts_report_configs: '[]' state_tracking_enabled: true @@ -593,8 +591,8 @@ test: session_encryption_key: 27bad3c25711099429c1afdfd1890910f3b59f5a4faec1c85e945cb8b02b02f261ba501d99cfbb4fab394e0102de6fecf8ffe260f322f610db3e96b2a775c120 short_term_phone_otp_max_attempts: 100 skip_encryption_allowed_list: '[]' - socure_webhook_secret_key: 'secret-key' - socure_webhook_secret_key_queue: '["old-key-one", "old-key-two"]' + socure_docv_webhook_secret_key: 'secret-key' + socure_docv_webhook_secret_key_queue: '["old-key-one", "old-key-two"]' team_ada_email: 'ada@example.com' team_all_login_emails: '["b@example.com", "c@example.com"]' team_daily_fraud_metrics_emails: '["g@example.com", "h@example.com"]' diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 3de48d0a232..ea6d23bfc8d 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -413,18 +413,16 @@ def self.store config.add(:skip_encryption_allowed_list, type: :json) config.add(:recommend_webauthn_platform_for_sms_ab_test_account_creation_percent, type: :integer) config.add(:recommend_webauthn_platform_for_sms_ab_test_authentication_percent, type: :integer) - config.add(:socure_document_request_endpoint, type: :string) - config.add(:socure_enabled, type: :boolean) + config.add(:socure_docv_document_request_endpoint, type: :string) + config.add(:socure_docv_enabled, type: :boolean) + config.add(:socure_docv_webhook_secret_key_queue, type: :json) + config.add(:socure_docv_webhook_secret_key, type: :string) config.add(:socure_idplus_api_key, type: :string) config.add(:socure_idplus_base_url, type: :string) config.add(:socure_idplus_timeout_in_seconds, type: :integer) config.add(:socure_reason_code_api_key, type: :string) config.add(:socure_reason_code_base_url, type: :string) config.add(:socure_reason_code_timeout_in_seconds, type: :integer) - config.add(:socure_standard_capture_desktop_enabled, type: :boolean) - config.add(:socure_webhook_enabled, type: :boolean) - config.add(:socure_webhook_secret_key, type: :string) - config.add(:socure_webhook_secret_key_queue, type: :json) config.add(:sp_handoff_bounce_max_seconds, type: :integer) config.add(:sp_issuer_user_counts_report_configs, type: :json) config.add(:state_tracking_enabled, type: :boolean) diff --git a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb index 35c205101d2..60b77e84bdb 100644 --- a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb +++ b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb @@ -7,7 +7,7 @@ let(:fake_socure_endpoint) { 'https://fake-socure.test' } let(:user) { create(:user) } let(:stored_result) { nil } - let(:socure_enabled) { true } + let(:socure_docv_enabled) { true } let(:document_capture_session) do DocumentCaptureSession.create( @@ -18,9 +18,9 @@ let(:document_capture_session_uuid) { document_capture_session&.uuid } before do - allow(IdentityConfig.store).to receive(:socure_enabled). - and_return(socure_enabled) - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_enabled). + and_return(socure_docv_enabled) + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor) @@ -179,7 +179,7 @@ end context 'when socure is disabled' do - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } it 'the webhook route does not exist' do get(:show) @@ -212,7 +212,7 @@ } end before do - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) end it 'connection timeout still responds to user' do @@ -276,7 +276,7 @@ end context 'when socure is disabled' do - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } it 'the webhook route does not exist' do get(:update) diff --git a/spec/controllers/idv/socure/document_capture_controller_spec.rb b/spec/controllers/idv/socure/document_capture_controller_spec.rb index f0fc04b39e0..e431b493a6a 100644 --- a/spec/controllers/idv/socure/document_capture_controller_spec.rb +++ b/spec/controllers/idv/socure/document_capture_controller_spec.rb @@ -17,7 +17,7 @@ attention_with_barcode: false, ) end - let(:socure_enabled) { true } + let(:socure_docv_enabled) { true } let(:document_capture_session) do DocumentCaptureSession.create( @@ -27,9 +27,9 @@ end before do - allow(IdentityConfig.store).to receive(:socure_enabled). - and_return(socure_enabled) - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_enabled). + and_return(socure_docv_enabled) + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor) @@ -194,7 +194,7 @@ end context 'when socure is disabled' do - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } it 'the webhook route does not exist' do get(:show) @@ -227,7 +227,7 @@ } end before do - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) end it 'connection timeout still responds to user' do @@ -313,7 +313,7 @@ end context 'when socure is disabled' do - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } it 'the webhook route does not exist' do get(:update) diff --git a/spec/controllers/socure_webhook_controller_spec.rb b/spec/controllers/socure_webhook_controller_spec.rb index ac6ea6373fc..a4f5ae0afce 100644 --- a/spec/controllers/socure_webhook_controller_spec.rb +++ b/spec/controllers/socure_webhook_controller_spec.rb @@ -6,7 +6,7 @@ describe 'POST /api/webhooks/socure/event' do let(:socure_secret_key) { 'this-is-a-secret' } let(:socure_secret_key_queue) { ['this-is-an-old-secret', 'this-is-an-older-secret'] } - let(:socure_enabled) { true } + let(:socure_docv_enabled) { true } let(:event_type) { 'TEST_WEBHOOK' } let(:event_docv_transaction_token) { 'TEST_WEBHOOK_TOKEN' } let(:customer_user_id) { '#1-customer' } @@ -32,12 +32,12 @@ end before do - allow(IdentityConfig.store).to receive(:socure_webhook_secret_key). + allow(IdentityConfig.store).to receive(:socure_docv_webhook_secret_key). and_return(socure_secret_key) - allow(IdentityConfig.store).to receive(:socure_webhook_secret_key_queue). + allow(IdentityConfig.store).to receive(:socure_docv_webhook_secret_key_queue). and_return(socure_secret_key_queue) - allow(IdentityConfig.store).to receive(:socure_enabled). - and_return(socure_enabled) + allow(IdentityConfig.store).to receive(:socure_docv_enabled). + and_return(socure_docv_enabled) allow(SocureDocvResultsJob).to receive(:perform_later) stub_analytics @@ -258,7 +258,7 @@ end context 'when socure webhook disabled' do - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } it 'the webhook route does not exist' do post :create, params: webhook_body diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb index ed52c7441fa..f6a26076917 100644 --- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb +++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb @@ -320,7 +320,7 @@ def verify_no_upload_photos_section_and_link(page) let(:sp_ipp_enabled) { true } let(:in_person_proofing_opt_in_enabled) { true } let(:facial_match_required) { true } - let(:socure_enabled) { false } + let(:socure_docv_enabled) { false } let(:doc_auth_vendor) { Idp::Constants::Vendors::MOCK } let(:desktop_test_mode_enabled) { false } let(:user) { user_with_2fa } @@ -331,7 +331,7 @@ def verify_no_upload_photos_section_and_link(page) service_provider.in_person_proofing_enabled = false service_provider.save! end - allow(IdentityConfig.store).to receive(:socure_enabled).and_return(socure_enabled) + allow(IdentityConfig.store).to receive(:socure_docv_enabled).and_return(socure_docv_enabled) allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(doc_auth_vendor) allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). and_return(desktop_test_mode_enabled) @@ -362,7 +362,7 @@ def verify_no_upload_photos_section_and_link(page) let(:facial_match_required) { false } let(:in_person_proofing_opt_in_enabled) { false } let(:sp_ipp_enabled) { false } - let(:socure_enabled) { true } + let(:socure_docv_enabled) { true } let(:doc_auth_vendor) { Idp::Constants::Vendors::SOCURE } context 'when socure desktop test mode is not enabled' do diff --git a/spec/features/idv/hybrid_mobile/entry_spec.rb b/spec/features/idv/hybrid_mobile/entry_spec.rb index 4261ce82806..7a979138551 100644 --- a/spec/features/idv/hybrid_mobile/entry_spec.rb +++ b/spec/features/idv/hybrid_mobile/entry_spec.rb @@ -23,7 +23,7 @@ context 'valid link' do before do - allow(IdentityConfig.store).to receive(:socure_enabled).and_return(true) + allow(IdentityConfig.store).to receive(:socure_docv_enabled).and_return(true) end it 'puts the user on the document capture page' do diff --git a/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb b/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb index 5b53406d281..fe21fec28d4 100644 --- a/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb +++ b/spec/features/idv/hybrid_mobile/hybrid_mobile_spec.rb @@ -10,7 +10,7 @@ before do allow(FeatureManagement).to receive(:doc_capture_polling_enabled?).and_return(true) - allow(IdentityConfig.store).to receive(:socure_enabled).and_return(true) + allow(IdentityConfig.store).to receive(:socure_docv_enabled).and_return(true) allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests).and_return(true) allow(Telephony).to receive(:send_doc_auth_link).and_wrap_original do |impl, config| @sms_link = config[:link] diff --git a/spec/services/doc_auth/socure/request_spec.rb b/spec/services/doc_auth/socure/request_spec.rb index 10f2cc1fad1..bab8c1901f9 100644 --- a/spec/services/doc_auth/socure/request_spec.rb +++ b/spec/services/doc_auth/socure/request_spec.rb @@ -14,7 +14,7 @@ let(:fake_metric_name) { 'fake metric' } before do - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) allow(request).to receive(:endpoint).and_return(fake_socure_endpoint) allow(request).to receive(:metric_name).and_return(fake_metric_name) diff --git a/spec/services/doc_auth/socure/requests/document_request_spec.rb b/spec/services/doc_auth/socure/requests/document_request_spec.rb index 6635c856550..8eeafaced02 100644 --- a/spec/services/doc_auth/socure/requests/document_request_spec.rb +++ b/spec/services/doc_auth/socure/requests/document_request_spec.rb @@ -47,7 +47,7 @@ let(:fake_socure_status) { 200 } before do - allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + allow(IdentityConfig.store).to receive(:socure_docv_document_request_endpoint). and_return(fake_socure_endpoint) stub_request(:post, fake_socure_endpoint). to_return( From 1934da1ab32824077fcd177290d6e71414a4f306 Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Tue, 19 Nov 2024 14:24:06 -0500 Subject: [PATCH 08/13] Fix a typo in the `ThreatMetrixPlugin` (#11526) It looks like in 1767dd586ed9fd1cab3662a90fece1e992d679a3 we introduced a typo where `request_ip` was spelled `request_id`. This resulted in us sending a null `request_ip` to Threatmetrix. This commit fixes the typo and modifies a spec to make sure the expected arguments are sent to the proofer. [skip changelog] --- .../proofing/resolution/plugins/threat_metrix_plugin.rb | 2 +- .../resolution/plugins/threatmetrix_plugin_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/services/proofing/resolution/plugins/threat_metrix_plugin.rb b/app/services/proofing/resolution/plugins/threat_metrix_plugin.rb index f16176b9911..a4be25bb0ef 100644 --- a/app/services/proofing/resolution/plugins/threat_metrix_plugin.rb +++ b/app/services/proofing/resolution/plugins/threat_metrix_plugin.rb @@ -24,7 +24,7 @@ def call( ddp_pii = applicant_pii.merge( threatmetrix_session_id: threatmetrix_session_id, email: user_email, - request_id: request_ip, + request_ip: request_ip, ) timer.time('threatmetrix') do diff --git a/spec/services/proofing/resolution/plugins/threatmetrix_plugin_spec.rb b/spec/services/proofing/resolution/plugins/threatmetrix_plugin_spec.rb index f59639e4a47..210a4497696 100644 --- a/spec/services/proofing/resolution/plugins/threatmetrix_plugin_spec.rb +++ b/spec/services/proofing/resolution/plugins/threatmetrix_plugin_spec.rb @@ -44,7 +44,12 @@ def sp_cost_count it 'calls the ThreatMetrix proofer' do call - expect(plugin.proofer).to have_received(:proof) + expect(plugin.proofer).to have_received(:proof).with( + **applicant_pii, + threatmetrix_session_id: threatmetrix_session_id, + email: user_email, + request_ip: request_ip, + ) end it 'creates a ThreatMetrix associated cost' do From 76219322d95c0e30ffae0827b16ab600aeb6e6c6 Mon Sep 17 00:00:00 2001 From: Shane Chesnutt Date: Tue, 19 Nov 2024 14:43:27 -0500 Subject: [PATCH 09/13] LG-14828 Remove deprecated state ID step routes (#11510) changelog: Internal, In-person proofing, Remove deprecated state ID step routes `verify/in_person_proofing/state_id` --- config/routes.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 0d832c5e8bf..b6336f12b70 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -410,10 +410,6 @@ # sometimes underscores get messed up when linked to via SMS as: :capture_doc_dashes - # Deprecated route - temporary redirect while state id changes are rolled out - get '/in_person_proofing/state_id' => redirect('verify/in_person/state_id', status: 307) - put '/in_person_proofing/state_id' => 'in_person/state_id#update' - get '/in_person' => 'in_person#index' get '/in_person/ready_to_verify' => 'in_person/ready_to_verify#show', as: :in_person_ready_to_verify From aa5a7e9c4ccf9e22f4588632fcce50157365a45e Mon Sep 17 00:00:00 2001 From: Jenny Verdeyen Date: Tue, 19 Nov 2024 16:51:34 -0500 Subject: [PATCH 10/13] LG-11666: Add PO search link to barcode page and ready to verify emails (#11520) changelog: In-person proofing, User-Facing Improvements, Add PO search link to barcode page and ready to verify emails --- app/services/marketing_site.rb | 1 + .../in_person/ready_to_verify/show.html.erb | 17 + .../_in_person_ready_to_verify.html.erb | 17 + config/locales/en.yml | 5 +- config/locales/es.yml | 5 +- config/locales/fr.yml | 5 +- config/locales/zh.yml | 5 +- spec/mailers/user_mailer_spec.rb | 56 ++- .../ready_to_verify/show.html.erb_spec.rb | 445 +++++++++++------- 9 files changed, 372 insertions(+), 184 deletions(-) diff --git a/app/services/marketing_site.rb b/app/services/marketing_site.rb index ce70067eb7f..67b48f7ff64 100644 --- a/app/services/marketing_site.rb +++ b/app/services/marketing_site.rb @@ -17,6 +17,7 @@ class UnknownArticleException < StandardError; end verify-your-identity/phone-number verify-your-identity/verify-your-address-by-mail verify-your-identity/overview + verify-your-identity/verify-your-identity-in-person/find-a-participating-post-office ].to_set.freeze def self.locale_segment diff --git a/app/views/idv/in_person/ready_to_verify/show.html.erb b/app/views/idv/in_person/ready_to_verify/show.html.erb index 0e03425babd..f268492a311 100644 --- a/app/views/idv/in_person/ready_to_verify/show.html.erb +++ b/app/views/idv/in_person/ready_to_verify/show.html.erb @@ -228,6 +228,23 @@ <% end %> + +<% if !@is_enhanced_ipp %> +

<%= t('in_person_proofing.body.location.change_location_heading') %>

+

+ <%= t( + 'in_person_proofing.body.location.change_location_info_html', + find_other_locations_link_html: link_to( + t('in_person_proofing.body.location.change_location_find_other_locations'), + help_center_redirect_url( + category: 'verify-your-identity', + article: 'verify-your-identity-in-person/find-a-participating-post-office', + ), + ), + ) %> +

+<% end %> +

<%= t('in_person_proofing.body.expect.heading') %>

<%= t('in_person_proofing.body.expect.info') %>

diff --git a/app/views/user_mailer/shared/_in_person_ready_to_verify.html.erb b/app/views/user_mailer/shared/_in_person_ready_to_verify.html.erb index b64af462add..d61de5bf003 100644 --- a/app/views/user_mailer/shared/_in_person_ready_to_verify.html.erb +++ b/app/views/user_mailer/shared/_in_person_ready_to_verify.html.erb @@ -275,5 +275,22 @@ <% end %> +<% if !@is_enhanced_ipp %> +

<%= t('in_person_proofing.body.location.change_location_heading') %>

+

+ <%= t( + 'in_person_proofing.body.location.change_location_info_html', + find_other_locations_link_html: link_to( + t('in_person_proofing.body.location.change_location_find_other_locations'), + help_center_redirect_url( + category: 'verify-your-identity', + article: 'verify-your-identity-in-person/find-a-participating-post-office', + ), + ), + ).html_safe %> +

+<% end %> + +

<%= t('in_person_proofing.body.expect.heading') %>

<%= t('in_person_proofing.body.expect.info') %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index f0ba426fa0e..f528ad32963 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1212,10 +1212,13 @@ in_person_proofing.body.cta.button: Try in person in_person_proofing.body.cta.prompt_detail: Most people who are unable to complete this step online are successful in verifying their identity at a participating Post Office. No appointment needed. Locations are available nationwide. in_person_proofing.body.expect.heading: What to expect after your visit in_person_proofing.body.expect.info: We’ll send you an email to let you know if your identity verification was successful or unsuccessful within 24 hours of your visit to the Post Office. +in_person_proofing.body.location.change_location_find_other_locations: Find other participating Post Office locations. +in_person_proofing.body.location.change_location_heading: Need to change your Post Office location? +in_person_proofing.body.location.change_location_info_html: You don’t need to create a new barcode, you can bring this barcode to any participating Post Office location. %{find_other_locations_link_html} in_person_proofing.body.location.distance.one: '%{count} mile away' in_person_proofing.body.location.distance.other: '%{count} miles away' in_person_proofing.body.location.heading: Post Office information -in_person_proofing.body.location.info: No appointment is needed to verify your identity. You can visit any participating Post Office location. +in_person_proofing.body.location.info: No appointment is needed to verify your identity. in_person_proofing.body.location.inline_error: Enter a valid address with city, state, and ZIP code in_person_proofing.body.location.location_button: Select in_person_proofing.body.location.po_search.address_label: Address diff --git a/config/locales/es.yml b/config/locales/es.yml index 9e1e3fe49f0..3640e2aa666 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1223,10 +1223,13 @@ in_person_proofing.body.cta.button: Intentar en persona in_person_proofing.body.cta.prompt_detail: La mayoría de las personas que no pueden hacer la verificación de su identidad en línea logran verificarla en una oficina de correos participante. No es necesario hacer cita para ello, y hay oficinas en todo el país. in_person_proofing.body.expect.heading: Qué esperar después de la visita in_person_proofing.body.expect.info: En las 24 horas siguientes a su visita a la oficina de correos, recibirá un correo electrónico para informarle si se logró o no su verificación de identidad. +in_person_proofing.body.location.change_location_find_other_locations: Busque otras oficinas de correos participantes. +in_person_proofing.body.location.change_location_heading: ¿Necesita cambiar su oficina de correos? +in_person_proofing.body.location.change_location_info_html: No necesita crear un nuevo código de barras, puede llevar este código de barras a cualquier oficina de correos participante. %{find_other_locations_link_html} in_person_proofing.body.location.distance.one: A %{count} milla de distancia in_person_proofing.body.location.distance.other: A %{count} millas de distancia in_person_proofing.body.location.heading: Información de la oficina de correos -in_person_proofing.body.location.info: No necesita hacer una cita para verificar su identidad. Puede acudir a cualquier oficina de correos participante. +in_person_proofing.body.location.info: No necesita hacer una cita para verificar su identidad. in_person_proofing.body.location.inline_error: Ingrese una dirección válida que incluya ciudad, estado y código postal in_person_proofing.body.location.location_button: Seleccionar in_person_proofing.body.location.po_search.address_label: Dirección diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 88b3c118e60..72ae134d8f5 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1212,10 +1212,13 @@ in_person_proofing.body.cta.button: Essayer en personne in_person_proofing.body.cta.prompt_detail: La plupart des personnes qui ne parviennent pas à effectuer cette étape en ligne réussissent à confirmer leur identité dans un bureau de poste participant. Sans rendez-vous. Il existe des sites dans l’ensemble du pays. in_person_proofing.body.expect.heading: Que faire après votre visite in_person_proofing.body.expect.info: Nous vous enverrons un e-mail pour vous informer de la réussite ou de l’échec de la vérification de votre identité dans les 24 heures suivant votre visite au bureau de poste. +in_person_proofing.body.location.change_location_find_other_locations: Trouvez d’autres bureaux de poste participants. +in_person_proofing.body.location.change_location_heading: Vous devez changer de bureau de poste ? +in_person_proofing.body.location.change_location_info_html: Pas besoin de créer un nouveau code-barres. Vous pouvez apporter le vôtre à n’importe quel bureau de poste participant. %{find_other_locations_link_html} in_person_proofing.body.location.distance.one: à %{count} mile in_person_proofing.body.location.distance.other: à %{count} miles in_person_proofing.body.location.heading: Informations sur les bureaux de poste -in_person_proofing.body.location.info: Aucun rendez-vous n’est nécessaire pour vérifier votre identité. Vous pouvez vous rendre dans n’importe quel bureau de poste participant. +in_person_proofing.body.location.info: Aucun rendez-vous n’est nécessaire pour vérifier votre identité. in_person_proofing.body.location.inline_error: Saisissez une adresse valide avec la ville, l’État et le code postal in_person_proofing.body.location.location_button: Sélectionner in_person_proofing.body.location.po_search.address_label: Adresse diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 6e7f1a67663..62cd87be425 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1225,10 +1225,13 @@ in_person_proofing.body.cta.button: 尝试亲身去 in_person_proofing.body.cta.prompt_detail: 无法在网上完成这一步骤的大多数人都能在一个参与本项目的邮局成功地证实身份。去邮局无需预约。全国各地都有参与本项目的邮局。 in_person_proofing.body.expect.heading: 去邮局后会发生什么 in_person_proofing.body.expect.info: 你去邮局后 24 小时内,我们会给你发电邮,告诉你是否成功验证了身份。 +in_person_proofing.body.location.change_location_find_other_locations: 查找其他参与本项目的邮局地点。 +in_person_proofing.body.location.change_location_heading: 需要更改你的邮局地点吗? +in_person_proofing.body.location.change_location_info_html: 你不需要创建新的条形码,你可以将这个条形码带到任何参与本项目的邮局。 %{find_other_locations_link_html} in_person_proofing.body.location.distance.one: 距离你 %{count} 英里 in_person_proofing.body.location.distance.other: 距离你 %{count} 英里 in_person_proofing.body.location.heading: 邮局信息 -in_person_proofing.body.location.info: 验证你的身份无需做预约。你可以去任何参与邮局。 +in_person_proofing.body.location.info: 验证你的身份无需做预约。 in_person_proofing.body.location.inline_error: 请输入正确地址,包括城市、州和邮编 in_person_proofing.body.location.location_button: 选择 in_person_proofing.body.location.po_search.address_label: 地址 diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index fef65205fe3..15a4ffb1fda 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' RSpec.describe UserMailer, type: :mailer do + include ActionView::Helpers::UrlHelper + let(:user) { create(:user) } let(:email_address) { user.email_addresses.first } let(:banned_email) { 'banned_email+123abc@gmail.com' } @@ -762,7 +764,59 @@ def expect_email_body_to_have_help_and_contact_links end end - context 'For In-Person Proofing (IPP)' do + context 'Need to change location section' do + context 'when Enhanced IPP is not enabled' do + let(:is_enhanced_ipp) { false } + let(:mail) do + UserMailer.with(user: user, email_address: email_address).in_person_ready_to_verify( + enrollment: enhanced_ipp_enrollment, + is_enhanced_ipp: is_enhanced_ipp, + ) + end + it 'renders the change location heading' do + expect(mail.html_part.body).to have_content( + t('in_person_proofing.body.location.change_location_heading'), + ) + end + + it 'renders the change location info' do + expect(mail.html_part.body).to have_content( + t( + 'in_person_proofing.body.location.change_location_info_html', + find_other_locations_link_html: + t('in_person_proofing.body.location.change_location_find_other_locations'), + ), + ) + end + end + context 'when Enhanced IPP is enabled' do + let(:is_enhanced_ipp) { true } + let(:mail) do + UserMailer.with(user: user, email_address: email_address).in_person_ready_to_verify( + enrollment: enhanced_ipp_enrollment, + is_enhanced_ipp: is_enhanced_ipp, + ) + end + + it 'does not render the change location heading' do + expect(mail.html_part.body).not_to have_content( + t('in_person_proofing.body.location.change_location_heading'), + ) + end + + it 'does not render the change location info' do + expect(mail.html_part.body).not_to have_content( + t( + 'in_person_proofing.body.location.change_location_info_html', + find_other_locations_link_html: + t('in_person_proofing.body.location.change_location_find_other_locations'), + ), + ) + end + end + end + + context 'For Informed Delivery In-Person Proofing (ID-IPP)' do context 'template displays modified content' do it 'conditionally renders content in the what to expect section applicable to IPP' do aggregate_failures do diff --git a/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb index fcd6cc0592c..400994814d5 100644 --- a/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb +++ b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb @@ -38,13 +38,6 @@ allow(view).to receive(:step_indicator_steps).and_return(step_indicator_steps) end - it 'displays a link back to the service provider' do - render - - expect(rendered).to have_content(service_provider.friendly_name) - expect(rendered).to have_css("lg-click-observer[event-name='#{sp_event_name}']") - end - it 'displays a link back to the help center' do render @@ -59,16 +52,6 @@ ) end - context 'when the user is not coming from a service provider' do - let(:service_provider) { nil } - - it 'does not display a link back to a service provider' do - render - - expect(rendered).not_to have_content('You may now close this window or') - end - end - it 'renders a cancel link' do render @@ -84,86 +67,125 @@ expect(rendered).to have_content(t('in_person_proofing.process.barcode.caption_label')) end - context 'with enrollment where selected_location_details is present' do - it 'renders a location' do - render + context 'link back to the service provider' do + context 'when the user is coming from a service provider' do + it 'displays a link back to the service provider' do + render - expect(rendered).to have_content(t('in_person_proofing.body.barcode.retail_hours')) + expect(rendered).to have_content(service_provider.friendly_name) + expect(rendered).to have_css("lg-click-observer[event-name='#{sp_event_name}']") + end end - end - context 'with enrollment where selected_location_details is not present' do - let(:selected_location_details) { nil } + context 'when the user is not coming from a service provider' do + let(:service_provider) { nil } - it 'does not render a location' do - render + it 'does not display a link back to the service provider' do + render - expect(rendered).not_to have_content(t('in_person_proofing.body.barcode.retail_hours')) + expect(rendered).not_to have_css("lg-click-observer[event-name='#{sp_event_name}']") + expect(rendered).to have_content('You may now close this window') + end end end - context 'Outage message flag' do - before(:each) do - end + context 'location section' do + context 'when selected_location_details is present' do + it 'renders a location' do + render - let(:formatted_date) { 'Tuesday, October 31' } - let(:in_person_outage_emailed_by_date) { 'November 1, 2023' } - let(:in_person_outage_expected_update_date) { 'October 31, 2023' } + expect(rendered).to have_content(t('in_person_proofing.body.barcode.retail_hours')) + end + end - it 'renders the outage alert when flag is enabled' do - allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). - and_return(true) - allow(IdentityConfig.store).to receive(:in_person_outage_emailed_by_date). - and_return(in_person_outage_emailed_by_date) - allow(IdentityConfig.store).to receive(:in_person_outage_expected_update_date). - and_return(in_person_outage_expected_update_date) + context 'when selected_location_details is not present' do + let(:selected_location_details) { nil } - render + it 'does not render a location' do + render - expect(rendered).to have_content( - t( - 'idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title', - date: formatted_date, - ), - ) + expect(rendered).not_to have_content(t('in_person_proofing.body.barcode.retail_hours')) + end end + end - it 'does not render a warning when outage dates are not included' do - allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). - and_return(true) - allow(IdentityConfig.store).to receive(:in_person_outage_emailed_by_date). - and_return('') - allow(IdentityConfig.store).to receive(:in_person_outage_expected_update_date). - and_return('') - - expect(rendered).to_not have_content( - t( - 'idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title', - date: formatted_date, - ), - ) + context 'outage alert' do + context 'when the outage message flag is enabled' do + before do + allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). + and_return(true) + end + + context 'when the outage dates are included' do + let(:formatted_date) { 'Tuesday, October 31' } + let(:in_person_outage_emailed_by_date) { 'November 1, 2023' } + let(:in_person_outage_expected_update_date) { 'October 31, 2023' } + + before do + allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). + and_return(true) + allow(IdentityConfig.store).to receive(:in_person_outage_emailed_by_date). + and_return(in_person_outage_emailed_by_date) + allow(IdentityConfig.store).to receive(:in_person_outage_expected_update_date). + and_return(in_person_outage_expected_update_date) + end + + it 'renders the outage alert' do + render + + expect(rendered).to have_content( + t( + 'idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title', + date: formatted_date, + ), + ) + end + end + + context 'when the outage dates are not included' do + before do + allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). + and_return(true) + allow(IdentityConfig.store).to receive(:in_person_outage_emailed_by_date). + and_return('') + allow(IdentityConfig.store).to receive(:in_person_outage_expected_update_date). + and_return('') + end + + it 'does not render a warning' do + render + + expect(rendered).to_not have_content( + t('idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title'), + ) + end + end end - it 'does not render the outage alert when flag is disabled' do - allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). - and_return(false) + context 'when the outage message flag is disabled' do + before do + allow(IdentityConfig.store).to receive(:in_person_outage_message_enabled). + and_return(false) + end - render + it 'does not render the outage alert' do + render - expect(rendered).not_to have_content( - t('idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title'), - ) + expect(rendered).not_to have_content( + t('idv.failure.exceptions.in_person_outage_error_message.ready_to_verify.title'), + ) + end end end - context 'For IPP (Not Enhanced IPP)' do - let(:is_enhanced_ipp) { false } - before do - @is_enhanced_ipp = false - end + context 'what to expect section' do + context 'when Enhanced IPP is not enabled' do + let(:is_enhanced_ipp) { false } + before do + @is_enhanced_ipp = is_enhanced_ipp + end - context 'template displays modified content' do - it 'conditionally renders content in the what to expect section applicable to IPP' do + it 'conditionally renders content applicable to ID-IPP' do render aggregate_failures do @@ -180,71 +202,13 @@ end end - it 'renders Questions? and Learn more link only once' do - render - - expect(rendered).to have_content(t('in_person_proofing.body.barcode.questions')).once - expect(rendered).to have_link( - t('in_person_proofing.body.barcode.learn_more'), - href: help_center_redirect_url( - category: 'verify-your-identity', - article: 'verify-your-identity-in-person', - ), - ).once - end - - it 'renders the email sent success alert' do - render - - expect(rendered).to have_content(t('in_person_proofing.body.barcode.email_sent')) - end - - it 'template does not display Enhanced In-Person Proofing specific content' do - render - - aggregate_failures do - [ - t('in_person_proofing.headings.barcode_eipp'), - t('in_person_proofing.body.barcode.eipp_tag'), - t('in_person_proofing.headings.barcode_what_to_bring'), - t('in_person_proofing.body.barcode.eipp_what_to_bring'), - t('in_person_proofing.process.eipp_bring_id.heading'), - t('in_person_proofing.process.eipp_bring_id_with_current_address.heading'), - t('in_person_proofing.process.eipp_bring_id.info'), - t('in_person_proofing.process.real_id_and_supporting_docs.heading'), - t('in_person_proofing.process.real_id_and_supporting_docs.info'), - t('in_person_proofing.process.eipp_bring_id_plus_documents.heading'), - t('in_person_proofing.process.eipp_bring_id_plus_documents.info'), - t('in_person_proofing.process.eipp_state_id_passport.heading'), - t('in_person_proofing.process.eipp_state_id_passport.info'), - t('in_person_proofing.process.eipp_state_id_military_id.heading'), - t('in_person_proofing.process.eipp_state_id_military_id.info'), - t('in_person_proofing.process.eipp_state_id_supporting_docs.heading'), - t('in_person_proofing.process.eipp_state_id_supporting_docs.info'), - t('in_person_proofing.process.state_id.heading_eipp'), - t('in_person_proofing.process.state_id.info_eipp'), - ].each do |copy| - Array(copy).each do |part| - expect(rendered).to_not have_content(part) - end - end + context 'when Enhanced IPP is enabled' do + let(:is_enhanced_ipp) { true } + before do + @is_enhanced_ipp = is_enhanced_ipp end - t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| - expect(rendered).to_not have_content(strip_tags(item)) - end - end - end - - context 'For Enhanced In-Person Proofing (EIPP)' do - let(:is_enhanced_ipp) { true } - - before do - @is_enhanced_ipp = true - end - - context 'template displays modified content' do - it 'conditionally renders content in the what to expect section applicable to EIPP' do + it 'conditionally renders content applicable to EIPP' do render aggregate_failures do @@ -260,7 +224,9 @@ end end end + end + context 'Questions? and Learn more link' do it 'renders Questions? and Learn more link only once' do render @@ -273,74 +239,195 @@ ), ).once end + end + context 'email sent success alert' do it 'renders the email sent success alert' do render expect(rendered).to have_content(t('in_person_proofing.body.barcode.email_sent')) end + end - context 'template displays additional (EIPP specific) content' do + context 'GSA Enhanced Pilot Barcode tag' do + context 'when Enhanced IPP is enabled' do + let(:is_enhanced_ipp) { true } + before do + @is_enhanced_ipp = is_enhanced_ipp + end it 'renders GSA Enhanced Pilot Barcode tag' do render expect(rendered).to have_content(t('in_person_proofing.body.barcode.eipp_tag')) end + end + context 'when Enhanced IPP is not enabled' do + let(:is_enhanced_ipp) { false } + before do + @is_enhanced_ipp = is_enhanced_ipp + end - context 'What to bring to the Post Office section' do - it 'displays heading and body' do - render + it 'does not render GSA Enhanced Pilot Barcode tag' do + render - expect(rendered).to have_content(t('in_person_proofing.headings.barcode_what_to_bring')) - expect(rendered).to have_content(t('in_person_proofing.body.barcode.eipp_what_to_bring')) - end + expect(rendered).not_to have_content(t('in_person_proofing.body.barcode.eipp_tag')) + end + end + end - it 'renders Option 1 content' do - render + context 'What to bring to the Post Office section' do + context 'when Enhanced IPP is enabled' do + let(:is_enhanced_ipp) { true } + before do + @is_enhanced_ipp = is_enhanced_ipp + end + + it 'displays heading and body' do + render + + expect(rendered).to have_content(t('in_person_proofing.headings.barcode_what_to_bring')) + expect(rendered).to have_content(t('in_person_proofing.body.barcode.eipp_what_to_bring')) + end + + it 'renders Option 1 content' do + render - aggregate_failures do - [ - t('in_person_proofing.process.eipp_bring_id.heading'), - t('in_person_proofing.process.eipp_bring_id_with_current_address.heading'), - t('in_person_proofing.process.eipp_bring_id.info'), - t('in_person_proofing.process.real_id_and_supporting_docs.heading'), - t('in_person_proofing.process.real_id_and_supporting_docs.info'), - ].each do |copy| - Array(copy).each do |part| - expect(rendered).to have_content(part) - end + aggregate_failures do + [ + t('in_person_proofing.process.eipp_bring_id.heading'), + t('in_person_proofing.process.eipp_bring_id_with_current_address.heading'), + t('in_person_proofing.process.eipp_bring_id.info'), + t('in_person_proofing.process.real_id_and_supporting_docs.heading'), + t('in_person_proofing.process.real_id_and_supporting_docs.info'), + ].each do |copy| + Array(copy).each do |part| + expect(rendered).to have_content(part) end end + end - t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| - expect(rendered).to have_content(strip_tags(item)) - end + t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| + expect(rendered).to have_content(strip_tags(item)) end + end - it 'renders Option 2 content' do - render + it 'renders Option 2 content' do + render - aggregate_failures do - [ - t('in_person_proofing.process.eipp_bring_id_plus_documents.heading'), - t('in_person_proofing.process.eipp_bring_id_plus_documents.info'), - t('in_person_proofing.process.eipp_state_id_passport.heading'), - t('in_person_proofing.process.eipp_state_id_passport.info'), - t('in_person_proofing.process.eipp_state_id_military_id.heading'), - t('in_person_proofing.process.eipp_state_id_military_id.info'), - t('in_person_proofing.process.eipp_state_id_supporting_docs.heading'), - t('in_person_proofing.process.eipp_state_id_supporting_docs.info'), - ].each do |copy| - Array(copy).each do |part| - expect(rendered).to have_content(part) - end + aggregate_failures do + [ + t('in_person_proofing.process.eipp_bring_id_plus_documents.heading'), + t('in_person_proofing.process.eipp_bring_id_plus_documents.info'), + t('in_person_proofing.process.eipp_state_id_passport.heading'), + t('in_person_proofing.process.eipp_state_id_passport.info'), + t('in_person_proofing.process.eipp_state_id_military_id.heading'), + t('in_person_proofing.process.eipp_state_id_military_id.info'), + t('in_person_proofing.process.eipp_state_id_supporting_docs.heading'), + t('in_person_proofing.process.eipp_state_id_supporting_docs.info'), + ].each do |copy| + Array(copy).each do |part| + expect(rendered).to have_content(part) end end + end - t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| - expect(rendered).to have_content(strip_tags(item)) + t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| + expect(rendered).to have_content(strip_tags(item)) + end + end + end + + context 'when Enhanced IPP is not enabled' do + let(:is_enhanced_ipp) { false } + before do + @is_enhanced_ipp = is_enhanced_ipp + end + + it 'template does not display Enhanced In-Person Proofing what to bring content' do + render + + aggregate_failures do + [ + t('in_person_proofing.headings.barcode_eipp'), + t('in_person_proofing.headings.barcode_what_to_bring'), + t('in_person_proofing.body.barcode.eipp_what_to_bring'), + t('in_person_proofing.process.eipp_bring_id.heading'), + t('in_person_proofing.process.eipp_bring_id_with_current_address.heading'), + t('in_person_proofing.process.eipp_bring_id.info'), + t('in_person_proofing.process.real_id_and_supporting_docs.heading'), + t('in_person_proofing.process.real_id_and_supporting_docs.info'), + t('in_person_proofing.process.eipp_bring_id_plus_documents.heading'), + t('in_person_proofing.process.eipp_bring_id_plus_documents.info'), + t('in_person_proofing.process.eipp_state_id_passport.heading'), + t('in_person_proofing.process.eipp_state_id_passport.info'), + t('in_person_proofing.process.eipp_state_id_military_id.heading'), + t('in_person_proofing.process.eipp_state_id_military_id.info'), + t('in_person_proofing.process.eipp_state_id_supporting_docs.heading'), + t('in_person_proofing.process.eipp_state_id_supporting_docs.info'), + t('in_person_proofing.process.state_id.heading_eipp'), + t('in_person_proofing.process.state_id.info_eipp'), + ].each do |copy| + Array(copy).each do |part| + expect(rendered).to_not have_content(part) + end end end + + t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |item| + expect(rendered).to_not have_content(strip_tags(item)) + end + end + end + end + + context 'Need to Change Location section' do + context 'when Enhanced IPP is not enabled' do + let(:is_enhanced_ipp) { false } + before do + @is_enhanced_ipp = is_enhanced_ipp + end + + it 'renders the change location heading' do + render + + expect(rendered).to have_content( + t('in_person_proofing.body.location.change_location_heading'), + ) + end + + it 'renders the change location info' do + render + + expect(rendered).to have_content( + t( + 'in_person_proofing.body.location.change_location_info_html', + find_other_locations_link_html: + t('in_person_proofing.body.location.change_location_find_other_locations'), + ), + ) + end + end + + context 'when Enhanced IPP is enabled' do + let(:is_enhanced_ipp) { true } + before do + @is_enhanced_ipp = is_enhanced_ipp + end + + it 'does not render the change location heading' do + render + + expect(rendered).not_to have_content( + t('in_person_proofing.body.location.change_location_heading'), + ) + end + + it 'does not render the change location info' do + render + + expect(rendered).not_to have_content( + t('in_person_proofing.body.location.change_location_info_html'), + ) end end end From bf8e69658bebda6fbae05e83473feb027c2d2452 Mon Sep 17 00:00:00 2001 From: "Davida (she/they)" Date: Wed, 20 Nov 2024 09:04:49 -0500 Subject: [PATCH 11/13] Exclude stale sessions in IAL2 usage query (#11528) * changelog: Internal, Analytics, Exclude stale sessions in IAL2 usage query --- lib/reporting/protocols_report.rb | 9 +++++- spec/lib/reporting/protocols_report_spec.rb | 32 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/reporting/protocols_report.rb b/lib/reporting/protocols_report.rb index 5f83c29ab2e..fe1d333961a 100644 --- a/lib/reporting/protocols_report.rb +++ b/lib/reporting/protocols_report.rb @@ -282,11 +282,18 @@ def aal3_issuers_query end def facial_match_issuers_query - format(<<~QUERY) + params = { + events: quote([SAML_AUTH_EVENT, OIDC_AUTH_EVENT]), + } + # OIDC_AUTH_EVENT and SAML_AUTH_EVENTs are fired before the initiating + # session is stored. + # We are omitting those events to prevent false positives + format(<<~QUERY, params) fields coalesce(properties.event_properties.service_provider, properties.event_properties.client_id, properties.service_provider) as issuer + | filter name NOT IN %{events} | filter properties.sp_request.facial_match | display issuer | sort issuer diff --git a/spec/lib/reporting/protocols_report_spec.rb b/spec/lib/reporting/protocols_report_spec.rb index 6d39565bed9..7d9dbb12faa 100644 --- a/spec/lib/reporting/protocols_report_spec.rb +++ b/spec/lib/reporting/protocols_report_spec.rb @@ -113,6 +113,38 @@ it 'generates the tabular csv data' do expect(report.as_tables).to eq expected_tables end + + describe 'queries' do + let(:client) { report.cloudwatch_client } + let(:time_query) do + { + from: report.time_range.begin, + to: report.time_range.end, + } + end + before do + allow(client).to receive(:fetch).and_call_original + end + + it 'calls the cloudwatch client with the expected queries' do + report.as_tables + + %i[ + aal3_issuers_query + saml_signature_query + facial_match_issuers_query + id_token_hint_query + loa_issuers_query + protocol_query + saml_signature_query + ].each do |query| + expect(client).to have_received(:fetch).with( + query: report.public_send(query), + **time_query, + ) + end + end + end end describe '#as_emailable_reports' do From fa0524f750ddcc704d84f714fcca70f9c8b959cb Mon Sep 17 00:00:00 2001 From: Lauren George Date: Wed, 20 Nov 2024 11:15:59 -0500 Subject: [PATCH 12/13] LG-13761: ProofingComponents to store real vendor used to verify document PII (#11499) * LG-13761: ProofingComponents to store real vendor used to verify document PII **Why** * Prior to this change, we always stored 'aamva' in a `User`'s `proofing_components.source_check` column regardless of whether a call to AAMVA was made or not. Accuracy is good and storing the actual "vendor_name" will align our analytics across our events. **How** * Instead of hard-coding `vendor_name` in `proofing_components.source_check`, we dig into the result object (as produced by the `ResultAdjudicator`) for the actual vendor_name. This value should always be present even though we don't have any validators for it. * Adds `:source_check_vendor` field to `Idv::Session` to capture the vendor name actually utilized to validate the state ID attributes. * Pulling `vendor_name` from the result object makes testing a little messier, cleaning up the various vendor names in our specs should be done in future work. For now, a new constant with the known `source_check` names has been added (`Idv::Constants::Vendors::SOURCE_CHECK`). * No new specs have been added as the functionality hasn't changed, but the relevant specs, including one end-to-end feature test, have been updated to check the vendor name is in the expected list or matches a placeholder value. changelog: Internal, Analytics, Store correct vendor name in ProofingComponents --- .../concerns/idv/verify_info_concern.rb | 24 +++++++++-- .../idv/in_person/verify_info_controller.rb | 1 + app/controllers/idv/verify_info_controller.rb | 1 + app/services/idv/proofing_components.rb | 11 ++--- app/services/idv/session.rb | 41 +++++++++++++++++++ .../resolution/plugins/aamva_plugin.rb | 2 +- lib/idp/constants.rb | 3 ++ .../idv/verify_info_controller_spec.rb | 17 +++++--- spec/features/idv/analytics_spec.rb | 29 +++++++------ spec/features/idv/end_to_end_idv_spec.rb | 4 +- spec/features/idv/proofing_components_spec.rb | 1 + spec/services/idv/proofing_components_spec.rb | 16 ++++++++ 12 files changed, 118 insertions(+), 32 deletions(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 297ac4a2a95..f3c0ac15f3a 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -232,6 +232,7 @@ def async_state_done(current_async_state) if form_response.success? save_threatmetrix_status(form_response) + save_source_check_vendor(form_response) move_applicant_to_idv_session idv_session.mark_verify_info_step_complete! @@ -251,21 +252,38 @@ def save_threatmetrix_status(form_response) idv_session.threatmetrix_review_status = review_status end + def save_source_check_vendor(form_response) + vendor = form_response.extra.dig( + :proofing_results, + :context, + :stages, + :state_id, + :vendor_name, + ) + idv_session.source_check_vendor = vendor + end + def summarize_result_and_rate_limit(summary_result) proofing_results_exception = summary_result.extra.dig(:proofing_results, :exception) resolution_rate_limiter.increment! if proofing_results_exception.blank? if summary_result.success? - add_proofing_components + add_proofing_components(summary_result) else idv_failure(summary_result) end end - def add_proofing_components + def add_proofing_components(summary_result) ProofingComponent.create_or_find_by(user: current_user).update( resolution_check: Idp::Constants::Vendors::LEXIS_NEXIS, - source_check: Idp::Constants::Vendors::AAMVA, + source_check: summary_result.extra.dig( + :proofing_results, + :context, + :stages, + :state_id, + :vendor_name, + ), ) end diff --git a/app/controllers/idv/in_person/verify_info_controller.rb b/app/controllers/idv/in_person/verify_info_controller.rb index 1eb612bb664..999aa3addb2 100644 --- a/app/controllers/idv/in_person/verify_info_controller.rb +++ b/app/controllers/idv/in_person/verify_info_controller.rb @@ -46,6 +46,7 @@ def self.step_info idv_session.resolution_successful = nil idv_session.verify_info_step_document_capture_session_uuid = nil idv_session.threatmetrix_review_status = nil + idv_session.source_check_vendor = nil idv_session.applicant = nil end, ) diff --git a/app/controllers/idv/verify_info_controller.rb b/app/controllers/idv/verify_info_controller.rb index bc11819486b..283fd647efc 100644 --- a/app/controllers/idv/verify_info_controller.rb +++ b/app/controllers/idv/verify_info_controller.rb @@ -52,6 +52,7 @@ def self.step_info end, undo_step: ->(idv_session:, user:) do idv_session.resolution_successful = nil + idv_session.source_check_vendor = nil idv_session.address_edited = nil idv_session.verify_info_step_document_capture_session_uuid = nil idv_session.threatmetrix_review_status = nil diff --git a/app/services/idv/proofing_components.rb b/app/services/idv/proofing_components.rb index f5b2a6e64e3..21d097d4ab4 100644 --- a/app/services/idv/proofing_components.rb +++ b/app/services/idv/proofing_components.rb @@ -2,12 +2,7 @@ module Idv class ProofingComponents - def initialize( - idv_session:, - session:, - user:, - user_session: - ) + def initialize(idv_session:, session:, user:, user_session:) @idv_session = idv_session @session = session @user = user @@ -33,7 +28,8 @@ def document_type end def source_check - Idp::Constants::Vendors::AAMVA if idv_session.verify_info_step_complete? + idv_session.source_check_vendor.presence || + (idv_session.verify_info_step_complete? && Idp::Constants::Vendors::AAMVA) end def resolution_check @@ -67,7 +63,6 @@ def to_h address_check:, threatmetrix:, threatmetrix_review_status:, - }.compact end diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index abc19910394..d31c0d143a5 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -1,6 +1,46 @@ # frozen_string_literal: true module Idv + # @attr address_edited [Boolean, nil] + # @attr address_verification_mechanism [String, nil] + # @attr applicant [Struct, nil] + # @attr document_capture_session_uuid [String, nil] + # @attr flow_path [String, nil] + # @attr go_back_path [String, nil] + # @attr gpo_code_verified [Boolean, nil] + # @attr had_barcode_attention_error [Boolean, nil] + # @attr had_barcode_read_failure [Boolean, nil] + # @attr idv_consent_given [Boolean, nil] + # @attr idv_consent_given_at [String, nil] + # @attr idv_phone_step_document_capture_session_uuid [String, nil] + # @attr mail_only_warning_shown [Boolean, nil] + # @attr opted_in_to_in_person_proofing [Boolean, nil] + # @attr personal_key [String, nil] + # @attr personal_key_acknowledged [Boolean, nil] + # @attr phone_for_mobile_flow [String, nil] + # @attr previous_phone_step_params [Array] + # @attr previous_ssn [String, nil] + # @attr profile_id [String, nil] + # @attr proofing_started_at [String, nil] + # @attr redo_document_capture [Boolean, nil] + # @attr resolution_successful [Boolean, nil] + # @attr selfie_check_performed [Boolean, nil] + # @attr selfie_check_required [Boolean, nil] + # @attr skip_doc_auth [Boolean, nil] + # @attr skip_doc_auth_from_handoff [Boolean, nil] + # @attr skip_doc_auth_from_how_to_verify [Boolean, nil] + # @attr skip_hybrid_handoff [Boolean, nil] + # @attr source_check_vendor [String, nil] + # @attr ssn [String, nil] + # @attr threatmetrix_review_status [String, nil] + # @attr threatmetrix_session_id [String, nil] + # @attr user_phone_confirmation [Boolean, nil] + # @attr vendor_phone_confirmation [Boolean, nil] + # @attr verify_info_step_document_capture_session_uuid [String, nil] + # @attr welcome_visited [Boolean, nil] + # @attr_reader current_user [User] + # @attr_reader gpo_otp [String, nil] + # @attr_reader service_provider [ServiceProvider] class Session VALID_SESSION_ATTRIBUTES = %i[ address_edited @@ -25,6 +65,7 @@ class Session profile_id proofing_started_at redo_document_capture + source_check_vendor resolution_successful selfie_check_performed selfie_check_required diff --git a/app/services/proofing/resolution/plugins/aamva_plugin.rb b/app/services/proofing/resolution/plugins/aamva_plugin.rb index f516efb5386..979bd3fd5ef 100644 --- a/app/services/proofing/resolution/plugins/aamva_plugin.rb +++ b/app/services/proofing/resolution/plugins/aamva_plugin.rb @@ -59,7 +59,7 @@ def out_of_aamva_jurisdiction_result errors: {}, exception: nil, success: true, - vendor_name: 'UnsupportedJurisdiction', + vendor_name: Idp::Constants::Vendors::AAMVA_UNSUPPORTED_JURISDICTION, ) end diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb index d7ba925e42d..77510830474 100644 --- a/lib/idp/constants.rb +++ b/lib/idp/constants.rb @@ -16,6 +16,9 @@ module Vendors MOCK = 'mock' USPS = 'usps' AAMVA = 'aamva' + AAMVA_UNSUPPORTED_JURISDICTION = 'UnsupportedJurisdiction' + STATE_ID_MOCK = 'StateIdMock' + SOURCE_CHECK = [AAMVA, AAMVA_UNSUPPORTED_JURISDICTION, STATE_ID_MOCK].freeze end # US State and Territory codes are diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index 5b8e6e758c0..7dacf98adb1 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -396,10 +396,6 @@ end context 'for an aamva request' do - before do - allow(controller).to receive(:load_async_state).and_return(async_state) - end - let(:document_capture_session) do DocumentCaptureSession.create(user:) end @@ -407,7 +403,7 @@ let(:success) { true } let(:errors) { {} } let(:exception) { nil } - let(:vendor_name) { :aamva } + let(:vendor_name) { 'aamva_placeholder' } let(:async_state) do # Here we're trying to match the store to redis -> read from redis flow this data travels @@ -436,6 +432,10 @@ document_capture_session.load_proofing_result end + before do + allow(controller).to receive(:load_async_state).and_return(async_state) + end + context 'when aamva processes the request normally' do it 'redirect to phone confirmation url' do put :show @@ -458,7 +458,12 @@ event = @analytics.events['IdV: doc auth verify proofing results'].first state_id = event.dig(:proofing_results, :context, :stages, :state_id) - expect(state_id).to match(a_hash_including(state_id_type: 'drivers_license')) + expect(state_id).to match( + hash_including( + state_id_type: 'drivers_license', + vendor_name: 'aamva_placeholder', + ), + ) end end diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index 606eb9373ec..dbe44ada457 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -41,7 +41,7 @@ { document_check: 'mock', document_type: 'state_id', - source_check: 'aamva', + source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', @@ -590,54 +590,54 @@ }, 'IdV: phone confirmation form' => { success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', otp_delivery_preference: 'sms', - proofing_components: { document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } + proofing_components: { document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'StateIdMock' } }, 'IdV: phone confirmation vendor' => { success: true, errors: {}, vendor: { exception: nil, vendor_name: 'AddressMock', transaction_id: 'address-mock-transaction-id-123', timed_out: false, reference: '' }, new_phone_added: false, hybrid_handoff_phone_used: false, area_code: '202', country_code: 'US', phone_fingerprint: anything, - proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } + proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'StateIdMock' } }, 'IdV: phone confirmation otp sent' => { success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, - proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } + proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'StateIdMock' } }, 'IdV: phone confirmation otp visited' => { - proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' }, + proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'StateIdMock' }, }, 'IdV: phone confirmation otp submitted' => { success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, errors: {}, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, :idv_enter_password_visited => { address_verification_method: 'phone', - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' }, + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' }, }, :idv_enter_password_submitted => { success: true, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: true, proofing_workflow_time_in_seconds: 0.0, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: final resolution' => { success: true, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: true, proofing_workflow_time_in_seconds: 0.0, # NOTE: pending_profile_idv_level should be set here, a nil value is cached for current_user.pending_profile. profile_history: match_array(kind_of(Idv::ProfileLogging)), - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: personal key visited' => { in_person_verification_pending: true, address_verification_method: 'phone', encrypted_profiles_missing: false, pending_profile_idv_level: idv_level, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: personal key acknowledgment toggled' => { checked: true, pending_profile_idv_level: idv_level, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: personal key submitted' => { address_verification_method: 'phone', fraud_review_pending: false, fraud_rejection: false, in_person_verification_pending: true, pending_profile_idv_level: idv_level, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: in person ready to verify visited' => { pending_profile_idv_level: idv_level, - proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' }, + proofing_components: { document_check: 'usps', source_check: 'StateIdMock', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' }, }, 'IdV: user clicked what to bring link on ready to verify page' => {}, 'IdV: user clicked sp link on ready to verify page' => {}, @@ -974,6 +974,7 @@ end end end + context 'Hybrid flow' do context 'facial comparison not required - Happy path' do before do @@ -1038,6 +1039,7 @@ end end end + context 'facial comparison required - Happy path' do before do allow_any_instance_of(DocAuth::Response).to receive(:selfie_status).and_return(:success) @@ -1097,6 +1099,7 @@ end end end + context 'facial comparison not required - Happy path' do before do allow(Telephony).to receive(:send_doc_auth_link).and_wrap_original do |impl, config| diff --git a/spec/features/idv/end_to_end_idv_spec.rb b/spec/features/idv/end_to_end_idv_spec.rb index 8757ec07bdc..055e011cb7a 100644 --- a/spec/features/idv/end_to_end_idv_spec.rb +++ b/spec/features/idv/end_to_end_idv_spec.rb @@ -268,7 +268,9 @@ def validate_verify_info_page def validate_verify_info_submit(user) expect(page).to have_content(t('doc_auth.forms.doc_success')) expect(user.proofing_component.resolution_check).to eq(Idp::Constants::Vendors::LEXIS_NEXIS) - expect(user.proofing_component.source_check).to eq(Idp::Constants::Vendors::AAMVA) + expect(user.proofing_component.source_check).to satisfy do |v| + Idp::Constants::Vendors::SOURCE_CHECK.include?(v) + end end def validate_phone_page diff --git a/spec/features/idv/proofing_components_spec.rb b/spec/features/idv/proofing_components_spec.rb index a12f58ad57f..19975e08a5d 100644 --- a/spec/features/idv/proofing_components_spec.rb +++ b/spec/features/idv/proofing_components_spec.rb @@ -31,6 +31,7 @@ proofing_components = user.active_profile.proofing_components expect(proofing_components['document_check']).to eq('mock') expect(proofing_components['document_type']).to eq('state_id') + expect(proofing_components['source_check']).to eq('StateIdMock') end end end diff --git a/spec/services/idv/proofing_components_spec.rb b/spec/services/idv/proofing_components_spec.rb index 97e78cc5ab6..8c871f87725 100644 --- a/spec/services/idv/proofing_components_spec.rb +++ b/spec/services/idv/proofing_components_spec.rb @@ -38,6 +38,7 @@ allow(FeatureManagement).to receive(:proofing_device_profiling_collecting_enabled?). and_return(true) idv_session.threatmetrix_review_status = 'pass' + idv_session.source_check_vendor = 'aamva' end it 'returns expected result' do @@ -63,6 +64,7 @@ context 'in-person proofing' do context 'establishing' do let!(:enrollment) { create(:in_person_enrollment, :establishing, user:) } + it 'returns USPS' do expect(subject.document_check).to eql(Idp::Constants::Vendors::USPS) end @@ -70,6 +72,7 @@ context 'pending' do let!(:enrollment) { create(:in_person_enrollment, :pending, user:) } + it 'returns USPS' do expect(subject.document_check).to eql(Idp::Constants::Vendors::USPS) end @@ -80,13 +83,16 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return('test_vendor') end + context 'before doc auth complete' do it 'returns nil' do expect(subject.document_check).to be_nil end end + context 'after doc auth completed successfully' do let(:pii_from_doc) { Idp::Constants::MOCK_IDV_APPLICANT } + it 'returns doc auth vendor' do expect(subject.document_check).to eql('test_vendor') end @@ -98,6 +104,7 @@ context 'in-person proofing' do context 'establishing' do let!(:enrollment) { create(:in_person_enrollment, :establishing, user:) } + it 'returns nil' do expect(subject.document_type).to be_nil end @@ -105,6 +112,7 @@ context 'pending' do let!(:enrollment) { create(:in_person_enrollment, :pending, user:) } + it 'returns nil' do expect(subject.document_type).to be_nil end @@ -117,8 +125,10 @@ expect(subject.document_type).to be_nil end end + context 'after doc auth completed successfully' do let(:pii_from_doc) { Idp::Constants::MOCK_IDV_APPLICANT } + it 'returns doc auth vendor' do expect(subject.document_type).to eql('state_id') end @@ -134,6 +144,7 @@ context 'after verification' do before do idv_session.mark_verify_info_step_complete! + idv_session.source_check_vendor = 'aamva' end it 'returns aamva' do @@ -195,10 +206,12 @@ before do idv_session.threatmetrix_review_status = 'pass' end + it 'returns true' do expect(subject.threatmetrix).to be_truthy end end + context 'threatmetrix_review_status not present' do it 'returns nil' do expect(subject.threatmetrix).to be_nil @@ -216,6 +229,7 @@ before do idv_session.threatmetrix_review_status = 'pass' end + it 'returns false' do expect(subject.threatmetrix).to eql(false) end @@ -234,10 +248,12 @@ before do idv_session.threatmetrix_review_status = 'pass' end + it 'returns value' do expect(subject.threatmetrix_review_status).to eql('pass') end end + context 'threatmetrix_review_status not present in idv_session' do it 'returns nil' do expect(subject.threatmetrix_review_status).to be_nil From bcbedff2d809f149380de6c13e74b8a9d03c1ec0 Mon Sep 17 00:00:00 2001 From: Andrew Duthie <1779930+aduth@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:20:52 -0500 Subject: [PATCH 13/13] Revert "LG-14828 Remove deprecated state ID step routes (#11510)" (#11542) This reverts commit 76219322d95c0e30ffae0827b16ab600aeb6e6c6. --- config/routes.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index b6336f12b70..0d832c5e8bf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -410,6 +410,10 @@ # sometimes underscores get messed up when linked to via SMS as: :capture_doc_dashes + # Deprecated route - temporary redirect while state id changes are rolled out + get '/in_person_proofing/state_id' => redirect('verify/in_person/state_id', status: 307) + put '/in_person_proofing/state_id' => 'in_person/state_id#update' + get '/in_person' => 'in_person#index' get '/in_person/ready_to_verify' => 'in_person/ready_to_verify#show', as: :in_person_ready_to_verify