diff --git a/app/assets/images/email/info.png b/app/assets/images/email/info.png index 67da418b193..6acbec7fd01 100644 Binary files a/app/assets/images/email/info.png and b/app/assets/images/email/info.png differ diff --git a/app/assets/images/email/warning.png b/app/assets/images/email/warning.png index b746e680e84..0f948e08c0d 100644 Binary files a/app/assets/images/email/warning.png and b/app/assets/images/email/warning.png differ diff --git a/app/assets/stylesheets/email.css.scss b/app/assets/stylesheets/email.css.scss index acbd6d0c650..9c8f9cad514 100644 --- a/app/assets/stylesheets/email.css.scss +++ b/app/assets/stylesheets/email.css.scss @@ -117,27 +117,25 @@ h6 { } } -.info-alert, -.warning-alert { - padding: 0 units(0.5); +.usa-alert { + border-left: units($theme-alert-bar-width) solid; td { - padding: units(1.5); - padding-right: units(1); + padding: units(1.5) 0; - & + td { - padding-left: 0; - padding-right: units(1.5); + &:first-child { + padding-left: $alert-icon-optical-padding; + padding-right: units(1); } } -} -.info-alert { - background-color: color('info-lighter'); -} + &.usa-alert--info { + @include alert-status-wrapper-styles('info'); + } -.warning-alert { - background-color: color('warning-lighter'); + &.usa-alert--warning { + @include alert-status-wrapper-styles('warning'); + } } .process-list td { diff --git a/app/controllers/redirect/policy_controller.rb b/app/controllers/redirect/policy_controller.rb index 6f7c47adfef..ee8113ef7e7 100644 --- a/app/controllers/redirect/policy_controller.rb +++ b/app/controllers/redirect/policy_controller.rb @@ -3,10 +3,18 @@ module Redirect class PolicyController < RedirectController def show - redirect_to_and_log( - MarketingSite.security_and_privacy_practices_url, - tracker_method: analytics.method(:policy_redirect), - ) + redirect_to_and_log(policy_url, tracker_method: analytics.method(:policy_redirect)) + end + + private + + def policy_url + case params[:policy] + when 'privacy_act_statement' + MarketingSite.privacy_act_statement_url + else + MarketingSite.security_and_privacy_practices_url + end end end end diff --git a/app/forms/idv/api_image_upload_form.rb b/app/forms/idv/api_image_upload_form.rb index da4dffc4ca9..70228e8d25f 100644 --- a/app/forms/idv/api_image_upload_form.rb +++ b/app/forms/idv/api_image_upload_form.rb @@ -348,6 +348,7 @@ def update_analytics(client_response:, vendor_request_time_in_ms:) update_funnel(client_response) birth_year = client_response.pii_from_doc&.dob&.to_date&.year zip_code = client_response.pii_from_doc&.zipcode&.to_s&.strip&.slice(0, 5) + issue_year = client_response.pii_from_doc&.state_id_issued&.to_date&.year analytics.idv_doc_auth_submitted_image_upload_vendor( **client_response.to_h.merge( birth_year: birth_year, @@ -356,6 +357,7 @@ def update_analytics(client_response:, vendor_request_time_in_ms:) flow_path: params[:flow_path], vendor_request_time_in_ms: vendor_request_time_in_ms, zip_code: zip_code, + issue_year: issue_year, ).except(:classification_info). merge(acuant_sdk_upgrade_ab_test_data), ) diff --git a/app/javascript/packages/webauthn/enroll-webauthn-device.spec.ts b/app/javascript/packages/webauthn/enroll-webauthn-device.spec.ts index 2e0baaf57d2..16ab7c5c629 100644 --- a/app/javascript/packages/webauthn/enroll-webauthn-device.spec.ts +++ b/app/javascript/packages/webauthn/enroll-webauthn-device.spec.ts @@ -85,10 +85,10 @@ describe('enrollWebauthnDevice', () => { ], timeout: 800000, attestation: 'none', + hints: ['security-key'], authenticatorSelection: { userVerification: 'discouraged', authenticatorAttachment: 'cross-platform', - hints: ['security-key'], }, excludeCredentials: [ { @@ -139,9 +139,9 @@ describe('enrollWebauthnDevice', () => { expect(navigator.credentials.create).to.have.been.calledWithMatch({ publicKey: { + hints: ['client-device'], authenticatorSelection: { authenticatorAttachment: 'platform', - hints: ['client-device'], }, }, }); diff --git a/app/javascript/packages/webauthn/enroll-webauthn-device.ts b/app/javascript/packages/webauthn/enroll-webauthn-device.ts index 2dae75c71d3..bf19d2a75db 100644 --- a/app/javascript/packages/webauthn/enroll-webauthn-device.ts +++ b/app/javascript/packages/webauthn/enroll-webauthn-device.ts @@ -41,7 +41,7 @@ interface EnrollResult { transports?: string[]; } -interface AuthenticatorSelectionCriteriaWithHints extends AuthenticatorSelectionCriteria { +interface PublicKeyCredentialCreationOptionsWithHints extends PublicKeyCredentialCreationOptions { hints?: Array; } @@ -94,14 +94,14 @@ async function enrollWebauthnDevice({ pubKeyCredParams: SUPPORTED_ALGORITHMS.map((alg) => ({ alg, type: 'public-key' })), timeout: 800000, attestation: 'none', + hints, authenticatorSelection: { // Prevents user from needing to use PIN with Security Key userVerification: 'discouraged', authenticatorAttachment, - hints, - } as AuthenticatorSelectionCriteriaWithHints, + }, excludeCredentials, - }, + } as PublicKeyCredentialCreationOptionsWithHints, })) as PublicKeyCredential; const response = credential.response as AuthenticatorAttestationResponseBrowserSupport; diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb index c4d2e722cf2..be5e17a31de 100644 --- a/app/jobs/get_usps_proofing_results_job.rb +++ b/app/jobs/get_usps_proofing_results_job.rb @@ -103,7 +103,7 @@ def check_enrollment(enrollment) enrollment_outcomes[:enrollments_checked] += 1 response = proofer.request_proofing_results( - enrollment.unique_id, enrollment.enrollment_code + enrollment, ) rescue Faraday::BadRequestError => err # 400 status code. This is used for some status updates and some common client errors diff --git a/app/models/doc_auth_log.rb b/app/models/doc_auth_log.rb index 468f95a4705..24a8a296cad 100644 --- a/app/models/doc_auth_log.rb +++ b/app/models/doc_auth_log.rb @@ -12,10 +12,6 @@ class DocAuthLog < ApplicationRecord # rubocop:disable Rails/UnusedIgnoredColumns self.ignored_columns = [ :aamva, - :email_sent_view_at, - :email_sent_view_count, - :send_link_view_at, - :send_link_view_count, ] # rubocop:enable Rails/UnusedIgnoredColumns end diff --git a/app/policies/mfa_policy.rb b/app/policies/mfa_policy.rb index 6c217e3bb07..41440f3c699 100644 --- a/app/policies/mfa_policy.rb +++ b/app/policies/mfa_policy.rb @@ -15,6 +15,10 @@ def phishing_resistant_mfa_enabled? mfa_user.webauthn_configurations.present? end + def piv_cac_mfa_enabled? + mfa_user.piv_cac_configurations.present? + end + def multiple_factors_enabled? mfa_user.enabled_mfa_methods_count > 1 end diff --git a/app/presenters/two_factor_options_presenter.rb b/app/presenters/two_factor_options_presenter.rb index 21f7200b8da..6c97445e661 100644 --- a/app/presenters/two_factor_options_presenter.rb +++ b/app/presenters/two_factor_options_presenter.rb @@ -43,7 +43,12 @@ def all_options_sorted TwoFactorAuthentication::SetUpPivCacSelectionPresenter, TwoFactorAuthentication::SetUpBackupCodeSelectionPresenter, ].map do |klass| - klass.new(user:, piv_cac_required:, phishing_resistant_required:, user_agent:) + klass.new( + user:, + piv_cac_required: piv_cac_required?, + phishing_resistant_required: phishing_resistant_only?, + user_agent:, + ) end. partition(&:recommended?). flatten @@ -106,11 +111,13 @@ def skip_label private def piv_cac_required? - @piv_cac_required + @piv_cac_required && + !mfa_policy.piv_cac_mfa_enabled? end def phishing_resistant_only? - @phishing_resistant_required && !mfa_policy.phishing_resistant_mfa_enabled? + @phishing_resistant_required && + !mfa_policy.phishing_resistant_mfa_enabled? end def mfa_policy diff --git a/app/services/usps_in_person_proofing/mock/fixtures.rb b/app/services/usps_in_person_proofing/mock/fixtures.rb index 11a2aac5f62..264405aa734 100644 --- a/app/services/usps_in_person_proofing/mock/fixtures.rb +++ b/app/services/usps_in_person_proofing/mock/fixtures.rb @@ -115,10 +115,6 @@ def self.request_in_progress_proofing_results_response load_response_fixture('request_in_progress_proofing_results_response.json') end - def self.request_enrollment_code_response - load_response_fixture('request_enrollment_code_response.json') - end - def self.load_response_fixture(filename) path = File.join( File.dirname(__FILE__), diff --git a/app/services/usps_in_person_proofing/mock/proofer.rb b/app/services/usps_in_person_proofing/mock/proofer.rb index 10b5be38681..a7a55e9d136 100644 --- a/app/services/usps_in_person_proofing/mock/proofer.rb +++ b/app/services/usps_in_person_proofing/mock/proofer.rb @@ -40,7 +40,7 @@ def request_facilities(_location, is_enhanced_ipp) end end - def request_proofing_results(_unique_id, _enrollment_code) + def request_proofing_results(_enrollment) JSON.parse(Fixtures.request_passed_proofing_results_response) end end diff --git a/app/services/usps_in_person_proofing/mock/responses/request_enrollment_code_response.json b/app/services/usps_in_person_proofing/mock/responses/request_enrollment_code_response.json deleted file mode 100644 index b69126bbc0b..00000000000 --- a/app/services/usps_in_person_proofing/mock/responses/request_enrollment_code_response.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "enrollmentCode": "2048702198804358", - "responseMessage": "Applicant 123456789 successfully processed" -} diff --git a/app/services/usps_in_person_proofing/proofer.rb b/app/services/usps_in_person_proofing/proofer.rb index ccfd883c0ef..f94ad4a1040 100644 --- a/app/services/usps_in_person_proofing/proofer.rb +++ b/app/services/usps_in_person_proofing/proofer.rb @@ -73,19 +73,18 @@ def request_enroll(applicant, is_enhanced_ipp) end # Makes HTTP request to retrieve proofing status - # Requires the applicant's enrollment code and unique ID. + # Requires the applicant's InPersonEnrollment. # When proofing is complete the API returns 200 status. # If the applicant has not been to the post office, has proofed recently, # or there is another issue, the API returns a 400 status with an error message. - # @param unique_id [String] - # @param enrollment_code [String] + # param enrollment [InPersonEnrollment] # @return [Hash] API response - def request_proofing_results(unique_id, enrollment_code) + def request_proofing_results(enrollment) url = "#{root_url}/ivs-ippaas-api/IPPRest/resources/rest/getProofingResults" request_body = { - sponsorID: sponsor_id, - uniqueID: unique_id, - enrollmentCode: enrollment_code, + sponsorID: enrollment.sponsor_id.to_i, + uniqueID: enrollment.unique_id, + enrollmentCode: enrollment.enrollment_code, } faraday.post(url, request_body, dynamic_headers) do |req| @@ -93,25 +92,6 @@ def request_proofing_results(unique_id, enrollment_code) end.body end - # Makes HTTP request to retrieve enrollment code - # If an applicant has a currently valid enrollment code, it will be returned. - # If they do not, a new one will be generated and returned. USPS sends the applicant an email - # with instructions and the enrollment code. - # Requires the applicant's unique ID. - # @param unique_id [String] - # @return [Hash] API response - def request_enrollment_code(unique_id) - url = "#{root_url}/ivs-ippaas-api/IPPRest/resources/rest/requestEnrollmentCode" - request_body = { - sponsorID: sponsor_id, - uniqueID: unique_id, - } - - faraday.post(url, request_body, dynamic_headers) do |req| - req.options.context = { service_name: 'usps_enrollment_code' } - end.body - end - # Makes a request to retrieve a new OAuth token, caches it, and returns it. Tokens have # historically had 15 minute expirys # @return [String] the token diff --git a/app/views/account_reset/pending/confirm.html.erb b/app/views/account_reset/pending/confirm.html.erb index 3c94e90c97c..91ebf6892e5 100644 --- a/app/views/account_reset/pending/confirm.html.erb +++ b/app/views/account_reset/pending/confirm.html.erb @@ -1,11 +1,17 @@ <% self.title = t('account_reset.cancel_request.title') %> +<%= render PageHeadingComponent.new.with_content(t('account_reset.pending.header')) %> +

<%= t('account_reset.pending.confirm', interval: @account_reset_deletion_period_interval) %>

-<%= button_to( - account_reset_pending_cancel_path, - class: 'usa-button usa-button--wide usa-button--big margin-bottom-2', +<%= render ButtonComponent.new( + url: account_reset_pending_cancel_path, method: :post, - ) { t('forms.buttons.continue') } %> + class: 'margin-top-3 margin-bottom-2', + wide: true, + big: true, + ).with_content(t('account_reset.pending.cancel_request')) %> -<%= link_to(t('links.go_back'), account_reset_pending_path) %> +
+ <%= link_to(t('links.go_back'), account_reset_pending_path) %> +
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 0013f1d7ed5..931a5b88e35 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -77,7 +77,11 @@

<%= new_tab_link_to( t('notices.privacy.security_and_privacy_practices'), - MarketingSite.security_and_privacy_practices_url, + policy_redirect_url( + policy: :security_and_privacy_practices, + flow: :sign_in, + step: :sign_in, + ), ) %>

diff --git a/app/views/idv/agreement/show.html.erb b/app/views/idv/agreement/show.html.erb index 0171f613809..3f418bb277e 100644 --- a/app/views/idv/agreement/show.html.erb +++ b/app/views/idv/agreement/show.html.erb @@ -35,7 +35,12 @@

<%= new_tab_link_to( t('doc_auth.instructions.learn_more'), - policy_redirect_url(flow: :idv, step: :agreement, location: :consent), + policy_redirect_url( + policy: :security_and_privacy_practices, + flow: :idv, + step: :agreement, + location: :consent, + ), ) %>

diff --git a/app/views/idv/phone/new.html.erb b/app/views/idv/phone/new.html.erb index dc783f9a2c9..51058a5a326 100644 --- a/app/views/idv/phone/new.html.erb +++ b/app/views/idv/phone/new.html.erb @@ -85,7 +85,6 @@ <% end %> <% end %> -

<%= t('idv.titles.otp_delivery_method') %>

<%= t('idv.messages.otp_delivery_method_description') %>

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 31d9484affe..b64af462add 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 @@ -1,8 +1,8 @@ <% if @presenter.outage_message_enabled? %> - +
- <%= image_tag('email/warning.png', width: 16, height: 16, alt: '') %> + <%= image_tag('email/warning.png', width: 16, height: 14, alt: '', style: 'margin-top: 5px;') %> <%= render 'shared/outage_alert', date: @presenter.formatted_outage_expected_update_date, email_date: @presenter.formatted_outage_emailed_by_date %> @@ -35,10 +35,10 @@ <%# Alert %> - +
@@ -185,7 +185,7 @@

<%= t('in_person_proofing.process.eipp_state_id_supporting_docs.info') %>

    <% t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |doc| %> -
  • <%= doc %>
  • +
  • <%= doc %>
  • <% end %>
@@ -236,7 +236,7 @@
- <%= image_tag('email/info.png', width: 16, height: 16, alt: '') %> + <%= image_tag('email/info.png', width: 16, height: 16, alt: '', style: 'margin-top: 4px;') %>

<%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %>

@@ -103,7 +103,7 @@

<%= t('in_person_proofing.process.real_id_and_supporting_docs.info') %>

    <% t('in_person_proofing.process.eipp_state_id_supporting_docs.info_list').each do |doc| %> -
  • <%= doc %>
  • +
  • <%= doc %>
  • <% end %>
- + <% if !@is_enhanced_ipp %>

<%= t('in_person_proofing.body.barcode.questions') %> diff --git a/config/initializers/asset_tag_helper_patch.rb b/config/initializers/asset_tag_helper_patch.rb deleted file mode 100644 index a60c2950232..00000000000 --- a/config/initializers/asset_tag_helper_patch.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module ActionView - module Helpers - module AssetTagHelper - def image_alt(_src) - '' - end - end - end -end diff --git a/config/locales/en.yml b/config/locales/en.yml index f2ad58da7ca..9d2d934db18 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1120,13 +1120,13 @@ idv.messages.gpo.start_over_link_text: start over and verify with your new addre idv.messages.gpo.timeframe_html: You’ll get a letter with a verification code in 5 to 10 days. idv.messages.otp_delivery_method_description: If you entered a landline above, please select “Phone call” below. idv.messages.phone.alert_html: 'Enter a phone number that is:' -idv.messages.phone.description: We’ll check this number with records and send you a one-time code. This is to help verify your identity. +idv.messages.phone.description: We’ll check your number with records and send you a one-time code to verify your identity. idv.messages.phone.failed_number.alert_text: We couldn’t match you to this number. idv.messages.phone.failed_number.gpo_alert_html: Try another number or %{link_html}. idv.messages.phone.failed_number.gpo_verify_link: verify by mail idv.messages.phone.failed_number.try_again_html: Try another number. idv.messages.phone.rules: - - Based in the United States (including U.S. territories) + - Based in the United States - Your primary number (the one you use the most often) idv.messages.return_to_profile: '‹ Return to your %{app_name} profile' idv.messages.sessions.enter_password_message: When you re-enter your password, %{app_name} will protect the information you’ve given us, so that only you can access it. @@ -1137,7 +1137,6 @@ idv.titles.activated: Your identity has already been verified idv.titles.come_back_later: Your letter is on the way idv.titles.enter_password: Review and submit idv.titles.mail.verify: Verify your address -idv.titles.otp_delivery_method: How should we send a code? idv.titles.session.enter_password: Re-enter your %{app_name} password idv.titles.session.enter_password_letter: Re-enter your %{app_name} password to send your letter idv.titles.unavailable: We are working to resolve an error diff --git a/config/locales/es.yml b/config/locales/es.yml index 7418fda7a19..a47dbbb9baf 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1131,13 +1131,13 @@ idv.messages.gpo.start_over_link_text: empezar de nuevo y verificar con su nueva idv.messages.gpo.timeframe_html: Recibirá una carta con un código de verificación en un plazo de 5 a 10 días. idv.messages.otp_delivery_method_description: Si introdujo un teléfono fijo arriba, seleccione “Llamada telefónica” a continuación. idv.messages.phone.alert_html: 'Introduzca un número de teléfono que sea:' -idv.messages.phone.description: Comprobaremos este número con los registros y le enviaremos un código de un solo uso. Esto ayuda a verificar su identidad. +idv.messages.phone.description: Comprobaremos su número con los registros y le enviaremos un código de un solo uso para verificar su identidad. idv.messages.phone.failed_number.alert_text: No pudimos asociarlo a este número. idv.messages.phone.failed_number.gpo_alert_html: Intente con otro número o %{link_html}. idv.messages.phone.failed_number.gpo_verify_link: verificar por correo idv.messages.phone.failed_number.try_again_html: Intente con otro número. idv.messages.phone.rules: - - Con base en Estados Unidos (incluidos los territorios de EE.UU.) + - De los Estados Unidos - Su número principal (el que utiliza con más frecuencia) idv.messages.return_to_profile: '‹ Vuelva a su perfil de %{app_name}' idv.messages.sessions.enter_password_message: Cuando vuelva a ingresar su contraseña, %{app_name} protegerá la información que nos dio, para que solo usted pueda acceder a ella. @@ -1148,7 +1148,6 @@ idv.titles.activated: Ya se verificó su identidad idv.titles.come_back_later: Su carta está en camino idv.titles.enter_password: Revise y envíe idv.titles.mail.verify: Verifique su dirección -idv.titles.otp_delivery_method: '¿Cómo debemos enviar un código?' idv.titles.session.enter_password: Vuelva a ingresar su contraseña de %{app_name} idv.titles.session.enter_password_letter: Vuelva a ingresar su contraseña de %{app_name} para enviar su carta idv.titles.unavailable: Estamos trabajando para corregir un error diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 79c1070e394..bfc78f280ce 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1120,13 +1120,13 @@ idv.messages.gpo.start_over_link_text: recommencer et vérifier avec votre nouve idv.messages.gpo.timeframe_html: Vous recevrez une lettre avec un code de vérification dans un délai de 5 à 10 jours. idv.messages.otp_delivery_method_description: Si vous avez saisi une ligne fixe ci-dessus, veuillez sélectionner « Appel téléphonique » ci-dessous. idv.messages.phone.alert_html: 'Saisissez un numéro de téléphone qui est :' -idv.messages.phone.description: Nous vérifierons ce numéro dans nos données et vous enverrons un code à usage unique. L’objectif est d’aider à vérifier votre identité. +idv.messages.phone.description: Nous vérifierons votre numéro dans nos données enregistrées et vous enverrons un code à usage unique afin de confirmer votre identité. idv.messages.phone.failed_number.alert_text: Nous n’avons pas pu vous associer à ce numéro. idv.messages.phone.failed_number.gpo_alert_html: Essayez un autre numéro ou %{link_html}. idv.messages.phone.failed_number.gpo_verify_link: vérifiez par courrier idv.messages.phone.failed_number.try_again_html: Essayez un autre numéro. idv.messages.phone.rules: - - Basé aux Etats-Unis (y compris les territoires américains) + - Basé aux Etats-Unis - Votre numéro principal (celui que vous utilisez le plus souvent) idv.messages.return_to_profile: '‹ Revenir à votre profil %{app_name}' idv.messages.sessions.enter_password_message: Lorsque vous ressaisirez votre mot de passe, %{app_name} protégera les informations que vous nous aurez données pour que vous seul puissiez y accéder. @@ -1137,7 +1137,6 @@ idv.titles.activated: Votre identité a déjà été vérifiée idv.titles.come_back_later: Votre lettre est en route idv.titles.enter_password: Révisez et validez idv.titles.mail.verify: Vérifiez votre adresse -idv.titles.otp_delivery_method: Comment devrions-nous envoyer un code ? idv.titles.session.enter_password: Ressaisissez votre mot de passe %{app_name} idv.titles.session.enter_password_letter: Ressaisissez votre mot de passe %{app_name} pour envoyer votre lettre idv.titles.unavailable: Nous travaillons à la résolution d’une erreur diff --git a/config/locales/zh.yml b/config/locales/zh.yml index d47b66bad17..b983796d3ea 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1132,15 +1132,15 @@ idv.messages.gpo.start_over_html: 如果该地址不对,你需要%{start_over_ idv.messages.gpo.start_over_link_text: 重新开始并用你新地址进行验证。 idv.messages.gpo.timeframe_html: 你会在 5 到 10 天 里收到带有验证码 的信。 idv.messages.otp_delivery_method_description: 如果你在上面输入的是座机电话,请在下边选择“接听电话”。 -idv.messages.phone.alert_html: '输入一个这样的电话号码:' -idv.messages.phone.description: 我们会将该号码与记录核对并给你发个一次性代码。这是为了帮助你验证身份。 +idv.messages.phone.alert_html: '输入一个符合以下条件的电话号码:' +idv.messages.phone.description: 我们会将你的号码与记录核对并给你发个一次性代码来验证你的身份。 idv.messages.phone.failed_number.alert_text: 我们无法将你与该号码匹配。 idv.messages.phone.failed_number.gpo_alert_html: 试试 另一个 号码或者%{link_html}。 idv.messages.phone.failed_number.gpo_verify_link: 通过普通邮件验证 idv.messages.phone.failed_number.try_again_html: 试试 另一个 号码。 idv.messages.phone.rules: - - 美国的(包括美国属地) - - 你的主要号码(你最常用的) + - 美国国内电话号码 + - 你的主要号码(或者你最常用的号码) idv.messages.return_to_profile: '‹ 返回你的 %{app_name} 用户资料' idv.messages.sessions.enter_password_message: 你重新输入密码时, %{app_name} 会保护你给我们的信息,这样只有你能访问这些信息。 idv.messages.sessions.no_pii: 测试站点 - 请勿使用真实个人信息(仅为演示目的) - 测试站点 @@ -1150,7 +1150,6 @@ idv.titles.activated: 你的身份已验证 idv.titles.come_back_later: 你的信件已寄出。 idv.titles.enter_password: 审阅并提交 idv.titles.mail.verify: 验证你的地址 -idv.titles.otp_delivery_method: 我们应该如何发送代码? idv.titles.session.enter_password: 重新输入你的%{app_name}密码 idv.titles.session.enter_password_letter: 重新输入你的%{app_name}密码来给你发信 idv.titles.unavailable: 我们正在争取解决错误。 diff --git a/db/primary_migrate/20240807202012_drop_aamva_from_doc_auth_logs.rb b/db/primary_migrate/20240807202012_drop_aamva_from_doc_auth_logs.rb new file mode 100644 index 00000000000..f799f355c89 --- /dev/null +++ b/db/primary_migrate/20240807202012_drop_aamva_from_doc_auth_logs.rb @@ -0,0 +1,7 @@ +class DropAamvaFromDocAuthLogs < ActiveRecord::Migration[7.1] + def change + safety_assured do + remove_column :doc_auth_logs, :aamva, :boolean + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3a781f89d38..e1cdc08131f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_08_01_183410) do +ActiveRecord::Schema[7.1].define(version: 2024_08_07_202012) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" @@ -168,7 +168,6 @@ t.datetime "agreement_view_at", precision: nil t.integer "agreement_view_count", default: 0 t.string "state" - t.boolean "aamva" t.datetime "verify_submit_at", precision: nil t.integer "verify_phone_submit_count", default: 0 t.datetime "verify_phone_submit_at", precision: nil diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb index f5ab7b74995..b6984bab926 100644 --- a/spec/controllers/idv/image_uploads_controller_spec.rb +++ b/spec/controllers/idv/image_uploads_controller_spec.rb @@ -405,6 +405,7 @@ workflow: an_instance_of(String), birth_year: 1938, zip_code: '59010', + issue_year: 2019, ) expect(@analytics).to have_logged_event( @@ -551,6 +552,7 @@ vendor: nil, birth_year: 1938, zip_code: '12345', + issue_year: nil, ) expect(@analytics).to have_logged_event( @@ -649,6 +651,7 @@ vendor: nil, birth_year: 1938, zip_code: '12345', + issue_year: nil, ) expect(@analytics).to have_logged_event( @@ -747,6 +750,7 @@ vendor: nil, birth_year: 1938, zip_code: '12345', + issue_year: nil, ) expect(@analytics).to have_logged_event( @@ -842,6 +846,7 @@ vendor: nil, birth_year: nil, zip_code: '12345', + issue_year: nil, ) expect(@analytics).to have_logged_event( @@ -937,6 +942,7 @@ vendor: nil, birth_year: 1938, zip_code: '12345', + issue_year: nil, ) expect(@analytics).to have_logged_event( @@ -1060,6 +1066,7 @@ vendor: nil, birth_year: nil, zip_code: nil, + issue_year: nil, ) expect_funnel_update_counts(user, 1) @@ -1154,6 +1161,7 @@ workflow: an_instance_of(String), birth_year: nil, zip_code: nil, + issue_year: nil, ) expect_funnel_update_counts(user, 1) diff --git a/spec/controllers/redirect/policy_controller_spec.rb b/spec/controllers/redirect/policy_controller_spec.rb index 3a6256b8025..818e5d07bed 100644 --- a/spec/controllers/redirect/policy_controller_spec.rb +++ b/spec/controllers/redirect/policy_controller_spec.rb @@ -7,7 +7,8 @@ describe '#show' do let(:location_params) { { flow: 'flow', step: 'step', location: 'location', foo: 'bar' } } - it 'redirects to policy page' do + + it 'redirects to security and privacy practices policy page' do redirect_url = MarketingSite.security_and_privacy_practices_url get :show, params: location_params @@ -21,5 +22,43 @@ step: 'step', ) end + + context 'with security_and_privacy_practices policy parameter' do + let(:params) { location_params.merge(policy: :security_and_privacy_practices) } + + it 'redirects to security and privacy practices policy page' do + redirect_url = MarketingSite.security_and_privacy_practices_url + + get :show, params: location_params + + expect(response).to redirect_to redirect_url + expect(@analytics).to have_logged_event( + 'Policy Page Redirect', + flow: 'flow', + location: 'location', + redirect_url: redirect_url, + step: 'step', + ) + end + end + + context 'with privacy_act_statement policy parameter' do + let(:params) { location_params.merge(policy: :privacy_act_statement) } + + it 'redirects to privacy act statement policy page' do + redirect_url = MarketingSite.privacy_act_statement_url + + get :show, params: params + + expect(response).to redirect_to redirect_url + expect(@analytics).to have_logged_event( + 'Policy Page Redirect', + flow: 'flow', + location: 'location', + redirect_url: redirect_url, + step: 'step', + ) + end + end end end diff --git a/spec/features/account_reset/pending_request_spec.rb b/spec/features/account_reset/pending_request_spec.rb index 01a31923db0..9f7a72b9bca 100644 --- a/spec/features/account_reset/pending_request_spec.rb +++ b/spec/features/account_reset/pending_request_spec.rb @@ -31,7 +31,7 @@ expect(page).to have_content(t('account_reset.pending.header')) click_on t('account_reset.pending.cancel_request') - click_on t('forms.buttons.continue') + click_on t('account_reset.pending.cancel_request') expect(page).to have_content(t('account_reset.pending.canceled')) click_on t('links.continue_sign_in') diff --git a/spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb b/spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb index 189baf01f77..6394a7a4c3f 100644 --- a/spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb +++ b/spec/features/two_factor_authentication/multiple_mfa_sign_up_spec.rb @@ -2,6 +2,7 @@ RSpec.feature 'Multi Two Factor Authentication', allowed_extra_analytics: [:*] do include WebAuthnHelper + include OidcAuthHelper describe 'When the user has not set up 2FA' do let(:fake_analytics) { FakeAnalytics.new } @@ -291,6 +292,41 @@ end end + context 'User with phishing resistant service provider' do + it 'should show phishing option first then all mfa options for second mfa' do + allow(IdentityConfig.store). + to receive(:show_unsupported_passkey_platform_authentication_setup). + and_return(true) + + visit_idp_from_ial1_oidc_sp_requesting_phishing_resistant(prompt: 'select_account') + sign_up_and_set_password + mock_webauthn_setup_challenge + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.webauthn')) + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.piv_cac')) + expect(page). + to_not have_content(t('two_factor_authentication.two_factor_choice_options.auth_app')) + expect(page). + to_not have_content(t('two_factor_authentication.two_factor_choice_options.phone')) + select_2fa_option('webauthn_platform', visible: :all) + + click_continue + + mock_press_button_on_hardware_key_on_setup + + click_link t('mfa.add') + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.webauthn')) + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.piv_cac')) + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.auth_app')) + expect(page). + to have_content(t('two_factor_authentication.two_factor_choice_options.phone')) + end + end + def click_2fa_option(option) find("label[for='two_factor_options_form_selection_#{option}']").click end diff --git a/spec/features/users/sign_in_spec.rb b/spec/features/users/sign_in_spec.rb index 2228cf10436..0c17dd5c5ef 100644 --- a/spec/features/users/sign_in_spec.rb +++ b/spec/features/users/sign_in_spec.rb @@ -902,8 +902,9 @@ end end - context 'Recaptcha check fails' do + context 'reCAPTCHA check fails' do let(:user) { create(:user, :fully_registered) } + before do allow(FeatureManagement).to receive(:sign_in_recaptcha_enabled?).and_return(true) allow(IdentityConfig.store).to receive(:recaptcha_mock_validator).and_return(true) @@ -912,9 +913,37 @@ it 'redirects user to security check failed page' do visit new_user_session_path + + asserted_expected_user = false + fake_analytics = FakeAnalytics.new + allow_any_instance_of(ApplicationController).to receive(:analytics). + and_wrap_original do |original| + original_analytics = original.call + if original_analytics.request.params[:controller] == 'users/sessions' && + original_analytics.request.params[:action] == 'create' + expect(original_analytics.user).to eq(user) + asserted_expected_user = true + end + + fake_analytics + end + fill_in :user_recaptcha_mock_score, with: '0.1' fill_in_credentials_and_submit(user.email, user.password) - + expect(asserted_expected_user).to eq(true) + expect(fake_analytics).to have_logged_event( + 'reCAPTCHA verify result received', + recaptcha_result: { + assessment_id: kind_of(String), + success: true, + score: 0.1, + errors: [], + reasons: [], + }, + evaluated_as_valid: false, + score_threshold: 0.2, + form_class: 'RecaptchaMockForm', + ) expect(current_path).to eq sign_in_security_check_failed_path end end diff --git a/spec/forms/idv/api_image_upload_form_spec.rb b/spec/forms/idv/api_image_upload_form_spec.rb index 18f260fbd31..3ea7ec748b1 100644 --- a/spec/forms/idv/api_image_upload_form_spec.rb +++ b/spec/forms/idv/api_image_upload_form_spec.rb @@ -238,6 +238,7 @@ workflow: 'test_non_liveness_workflow', birth_year: 1938, zip_code: '59010', + issue_year: 2019, ) end @@ -343,6 +344,7 @@ workflow: 'test_liveness_workflow', birth_year: 1938, zip_code: '59010', + issue_year: 2019, ) end diff --git a/spec/policies/user_mfa_policy_spec.rb b/spec/policies/mfa_policy_spec.rb similarity index 75% rename from spec/policies/user_mfa_policy_spec.rb rename to spec/policies/mfa_policy_spec.rb index ce4951a52da..784877283e0 100644 --- a/spec/policies/user_mfa_policy_spec.rb +++ b/spec/policies/mfa_policy_spec.rb @@ -24,6 +24,20 @@ it { expect(subject.multiple_factors_enabled?).to eq true } end + describe '#piv_cac_mfa_enabled?' do + context 'with piv configuration' do + let(:user) { create(:user, :with_piv_or_cac) } + + it { expect(subject.piv_cac_mfa_enabled?).to eq true } + end + + context 'with non PIV MFA option' do + let(:user) { create(:user, :fully_registered) } + + it { expect(subject.piv_cac_mfa_enabled?).to eq false } + end + end + describe '#unphishable?' do context 'with unphishable configuration' do let(:user) { create(:user, :with_piv_or_cac, :with_webauthn) } diff --git a/spec/presenters/two_factor_options_presenter_spec.rb b/spec/presenters/two_factor_options_presenter_spec.rb index bde99540686..5816a8da51d 100644 --- a/spec/presenters/two_factor_options_presenter_spec.rb +++ b/spec/presenters/two_factor_options_presenter_spec.rb @@ -51,6 +51,56 @@ end end + context 'when a phishing-resistant SP but already has phishing-resistant mfa' do + let(:user) do + create( + :user, :fully_registered, :with_webauthn + ) + end + let(:presenter) do + described_class.new( + user_agent: user_agent, user: user, + phishing_resistant_required: true + ) + end + + it 'displays all options' do + expect(presenter.options.map(&:class)).to eq [ + TwoFactorAuthentication::SetUpWebauthnPlatformSelectionPresenter, + TwoFactorAuthentication::SetUpAuthAppSelectionPresenter, + TwoFactorAuthentication::SetUpPhoneSelectionPresenter, + TwoFactorAuthentication::SetUpWebauthnSelectionPresenter, + TwoFactorAuthentication::SetUpPivCacSelectionPresenter, + TwoFactorAuthentication::SetUpBackupCodeSelectionPresenter, + ] + end + end + + context 'with a PIV only SP but already has PIV mfa' do + let(:user) do + create( + :user, :fully_registered, :with_piv_or_cac + ) + end + let(:presenter) do + described_class.new( + user_agent: user_agent, user: user, + piv_cac_required: true + ) + end + + it 'displays all options' do + expect(presenter.options.map(&:class)).to eq [ + TwoFactorAuthentication::SetUpWebauthnPlatformSelectionPresenter, + TwoFactorAuthentication::SetUpAuthAppSelectionPresenter, + TwoFactorAuthentication::SetUpPhoneSelectionPresenter, + TwoFactorAuthentication::SetUpWebauthnSelectionPresenter, + TwoFactorAuthentication::SetUpPivCacSelectionPresenter, + TwoFactorAuthentication::SetUpBackupCodeSelectionPresenter, + ] + end + end + context 'when hide_phone_mfa_signup is enabled' do before do allow(IdentityConfig.store).to receive(:hide_phone_mfa_signup).and_return(true) diff --git a/spec/services/attribute_asserter_spec.rb b/spec/services/attribute_asserter_spec.rb index 7b5f5bca2f0..5ad52b0a9c0 100644 --- a/spec/services/attribute_asserter_spec.rb +++ b/spec/services/attribute_asserter_spec.rb @@ -3,7 +3,6 @@ RSpec.describe AttributeAsserter do include SamlAuthHelper - let(:ial1_user) { create(:user, :fully_registered) } let(:user) { create(:profile, :active, :verified).user } let(:biometric_verified_user) { create(:profile, :active, :verified, idv_level: :in_person).user } let(:user_session) { {} } @@ -26,134 +25,28 @@ metadata: {}, ) end - let(:raw_sp1_authn_request) do - sp1_authnrequest = saml_authn_request_url(overrides: { issuer: sp1_issuer }) - CGI.unescape sp1_authnrequest.split('SAMLRequest').last - end - let(:raw_aal3_sp1_authn_request) do - ial1_aal3_authnrequest = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, - ], - }, - ) - CGI.unescape ial1_aal3_authnrequest.split('SAMLRequest').last - end - let(:raw_ial1_authn_request) do - ial1_authn_request_url = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - }, - ) - CGI.unescape ial1_authn_request_url.split('SAMLRequest').last - end - let(:raw_vtr_no_proofing_authn_request) do - vtr_proofing_authn_request = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: 'C1.C2', - }, - ) - CGI.unescape vtr_proofing_authn_request.split('SAMLRequest').last - end - let(:raw_ial2_authn_request) do - ial2_authnrequest = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - }, - ) - CGI.unescape ial2_authnrequest.split('SAMLRequest').last - end - let(:raw_vtr_proofing_authn_request) do - vtr_proofing_authn_request = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: 'C1.C2.P1', - }, - ) - CGI.unescape vtr_proofing_authn_request.split('SAMLRequest').last - end - let(:raw_ial1_aal3_authn_request) do - ial1_aal3_authnrequest = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, - ], - }, - ) - CGI.unescape ial1_aal3_authnrequest.split('SAMLRequest').last - end - let(:raw_ialmax_authn_request) do - ialmax_authnrequest = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - ], - authn_context_comparison: 'minimum', - }, - ) - CGI.unescape ialmax_authnrequest.split('SAMLRequest').last - end - let(:raw_biometric_preferred_ial_authn_request) do - biometric_preferred_ial_authnrequest = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL2_BIO_PREFERRED_AUTHN_CONTEXT_CLASSREF, - ], - }, - ) - CGI.unescape biometric_preferred_ial_authnrequest.split('SAMLRequest').last + + let(:authn_context) do + [ + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ] end - let(:raw_biometric_required_ial_authn_request) do - biometric_preferred_ial_authnrequest = saml_authn_request_url( + let(:options) { { authn_context: } } + let(:raw_authn_request) do + raw = saml_authn_request_url( overrides: { issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL2_BIO_PREFERRED_AUTHN_CONTEXT_CLASSREF, - ], - }, + }.merge(options), ) - CGI.unescape biometric_preferred_ial_authnrequest.split('SAMLRequest').last - end - let(:sp1_authn_request) do - SamlIdp::Request.from_deflated_request(raw_sp1_authn_request) - end - let(:aal3_sp1_authn_request) do - SamlIdp::Request.from_deflated_request(raw_aal3_sp1_authn_request) - end - let(:ial1_authn_request) do - SamlIdp::Request.from_deflated_request(raw_ial1_authn_request) - end - let(:ial2_authn_request) do - SamlIdp::Request.from_deflated_request(raw_ial2_authn_request) - end - let(:vtr_proofing_authn_request) do - SamlIdp::Request.from_deflated_request(raw_vtr_proofing_authn_request) - end - let(:vtr_no_proofing_authn_request) do - SamlIdp::Request.from_deflated_request(raw_vtr_no_proofing_authn_request) - end - let(:ial1_aal3_authn_request) do - SamlIdp::Request.from_deflated_request(raw_ial1_aal3_authn_request) - end - let(:ialmax_authn_request) do - SamlIdp::Request.from_deflated_request(raw_ialmax_authn_request) - end - let(:biometric_preferred_ial_authnrequest) do - SamlIdp::Request.from_deflated_request(raw_biometric_preferred_ial_authn_request) + + CGI.unescape raw.split('SAMLRequest').last end - let(:biometric_required_ial_authnrequest) do - SamlIdp::Request.from_deflated_request(raw_biometric_required_ial_authn_request) + + let(:authn_request) do + SamlIdp::Request.from_deflated_request(raw_authn_request) end + let(:decrypted_pii) do Pii::Attributes.new_from_hash( first_name: 'Jåné', @@ -163,18 +56,25 @@ ) end + let(:subject) do + described_class.new( + user:, + name_id_format:, + service_provider:, + authn_request:, + decrypted_pii:, + user_session:, + ) + end + describe '#build' do context 'when an IAL2 request is made' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ial2_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:authn_context) do + [ + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + ] end + context 'when the user has been proofed without biometric' do context 'custom bundle includes email, phone, and first_name' do before do @@ -197,7 +97,7 @@ expect(get_asserted_attribute(user, :phone)).to eq '+18888675309' end - it 'gets UUID (MBUN) from Service Provider' do + it 'gets UUID from Service Provider' do expect(get_asserted_attribute(user, :uuid)).to eq user.last_identity.uuid end end @@ -261,20 +161,12 @@ context 'authn request specifies bundle' do # rubocop:disable Layout/LineLength - let(:raw_ial2_authn_request) do - request_url = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}first_name:last_name email, ssn", - "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}phone", - ], - }, - ) - CGI.unescape( - request_url.split('SAMLRequest').last, - ) + let(:authn_context) do + [ + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}first_name:last_name email, ssn", + "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}phone", + ] end # rubocop:enable Layout/LineLength @@ -346,8 +238,9 @@ end end end + context 'when the user has been proofed with biometric' do - let(:user) { biometric_verified_user } + let(:user) { create(:profile, :active, :verified, idv_level: :in_person).user } before do user.identities << identity subject.build @@ -361,16 +254,7 @@ end context 'verified user and proofing VTR request' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: vtr_proofing_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) - end + let(:authn_context) { 'C1.C2.P1' } before do user.identities << identity @@ -389,17 +273,6 @@ end context 'when an IAL1 request is made' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: sp1_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) - end - context 'when the user has been proofed without biometric comparison' do context 'custom bundle includes email, phone, and first_name' do before do @@ -418,8 +291,9 @@ expect(get_asserted_attribute(user, :email)).to eq expected_email end - it 'gets UUID (MBUN) from Service Provider' do - expect(get_asserted_attribute(user, :uuid)).to eq user.last_identity.uuid + it 'gets UUID from Service Provider' do + uuid_getter = user.asserted_attributes[:uuid][:getter] + expect(uuid_getter.call(user)).to eq user.last_identity.uuid end end @@ -437,11 +311,19 @@ it 'only includes uuid, email, aal, and ial (no verified_at)' do expect(user.asserted_attributes.keys).to eq %i[uuid email aal ial] end + + it 'does not create a getter function for IAL1 attributes' do + expected_email = EmailContext.new(user).last_sign_in_email_address.email + expect(get_asserted_attribute(user, :email)).to eq expected_email + end + + it 'gets UUID from Service Provider' do + expect(get_asserted_attribute(user, :uuid)).to eq user.last_identity.uuid + end end context 'the service provider is ial2' do let(:service_provider_ial) { 2 } - it 'includes verified_at' do expect(user.asserted_attributes.keys).to eq %i[uuid email verified_at aal ial] end @@ -461,25 +343,17 @@ end end + # rubocop:disable Layout/LineLength context 'authn request specifies bundle with first_name, last_name, email, ssn, phone' do - # rubocop:disable Layout/LineLength - let(:raw_sp1_authn_request) do - request_url = saml_authn_request_url( - overrides: { - issuer: sp1_issuer, - authn_context: [ - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, - "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}first_name:last_name email, ssn", - "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}phone", - ], - }, - ) - CGI.unescape( - request_url.split('SAMLRequest').last, - ) + let(:authn_context) do + [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}first_name:last_name email, ssn", + "#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}phone", + ] + # rubocop:enable Layout/LineLength end - # rubocop:enable Layout/LineLength it 'only includes uuid, email, aal, and ial' do expect(user.asserted_attributes.keys).to eq(%i[uuid email aal ial]) @@ -549,16 +423,7 @@ end context 'request made with a VTR param' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: vtr_no_proofing_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) - end + let(:options) { { authn_context: 'C1.C2' } } before do user.identities << identity @@ -575,8 +440,10 @@ end end end + context 'when the user has been proofed with biometric comparison' do - let(:user) { biometric_verified_user } + let(:user) { create(:profile, :active, :verified, idv_level: :in_person).user } + before do user.identities << identity subject.build @@ -592,15 +459,11 @@ context 'verified user and IAL1 AAL3 request' do context 'service provider configured for AAL3' do let(:service_provider_aal) { 3 } - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: aal3_sp1_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:authn_context) do + [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ] end before do @@ -617,15 +480,11 @@ end context 'service provider requests AAL3' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ial1_aal3_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:authn_context) do + [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ] end before do @@ -645,15 +504,13 @@ context 'IALMAX' do context 'service provider requests IALMAX with IAL1 user' do let(:service_provider_ial) { 2 } - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ialmax_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ], + authn_context_comparison: 'minimum', + } end before do @@ -674,15 +531,13 @@ context 'IAL2 service provider requests IALMAX with IAL2 user' do let(:service_provider_ial) { 2 } - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ialmax_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ], + authn_context_comparison: 'minimum', + } end before do @@ -707,15 +562,13 @@ context 'non-IAL2 service provider requests IALMAX with IAL2 user' do let(:service_provider_ial) { 1 } - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ialmax_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ], + authn_context_comparison: 'minimum', + } end before do @@ -738,15 +591,12 @@ end context 'when biometric IAL preferred is requested' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: biometric_preferred_ial_authnrequest, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL2_BIO_PREFERRED_AUTHN_CONTEXT_CLASSREF, + ], + } end context 'when the user has been proofed with biometric' do @@ -776,15 +626,12 @@ end context 'when biometric IAL required is requested' do - let(:subject) do - described_class.new( - user: user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: biometric_required_ial_authnrequest, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF, + ], + } end context 'when the user has been proofed with biometric comparison' do @@ -802,6 +649,8 @@ end shared_examples 'unverified user' do + let(:user) { create(:user, :fully_registered) } + context 'custom bundle does not include email, phone' do before do allow(service_provider.metadata).to receive(:[]).with(:attribute_bundle).and_return( @@ -811,7 +660,7 @@ end it 'only includes UUID, aal, and ial' do - expect(ial1_user.asserted_attributes.keys).to eq(%i[uuid aal ial]) + expect(user.asserted_attributes.keys).to eq(%i[uuid aal ial]) end end @@ -825,7 +674,7 @@ end it 'includes all the user email addresses' do - all_emails_getter = ial1_user.asserted_attributes[:all_emails][:getter] + all_emails_getter = user.asserted_attributes[:all_emails][:getter] emails = all_emails_getter.call(user) expect(emails.length).to eq(2) expect(emails).to match_array(user.confirmed_email_addresses.map(&:email)) @@ -841,42 +690,419 @@ end it 'only includes UUID, email, aal, and ial' do - expect(ial1_user.asserted_attributes.keys).to eq(%i[uuid email aal ial]) + expect(user.asserted_attributes.keys).to eq(%i[uuid email aal ial]) end end end context 'unverified user and IAL2 request' do - let(:subject) do - described_class.new( - user: ial1_user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ial2_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:user) { create(:user, :fully_registered) } + let(:authn_context) do + [ + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + ] end it_behaves_like 'unverified user' end context 'unverified user and LOA1 request' do - let(:subject) do - described_class.new( - user: ial1_user, - name_id_format: name_id_format, - service_provider: service_provider, - authn_request: ial1_authn_request, - decrypted_pii: decrypted_pii, - user_session: user_session, - ) + let(:user) { create(:user, :fully_registered) } + + let(:authn_context) do + [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ] end it_behaves_like 'unverified user' end end + describe 'aal attributes handling' do + before do + user.identities << identity + allow(service_provider.metadata).to receive(:[]).with(:attribute_bundle). + and_return(%w[email]) + subject.build + end + + describe 'when no aal requested' do + context 'default_aal is nil' do + let(:authn_context) { [] } + it 'asserts default aal' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 1' do + let(:service_provider_aal) { 1 } + let(:authn_context) { [] } + + it 'asserts aal1' do + # we do not enforce aal1, we enforce default aal, so this should be updated + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 2' do + let(:service_provider_aal) { 2 } + let(:authn_context) { [] } + + it 'asserts aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 3' do + let(:service_provider_aal) { 3 } + let(:authn_context) { [] } + + it 'asserts aa33' do + # we do not enforce aal3, we enforce aal2 with phishing-resistant mfa + # so should be updated + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + + describe 'when aal is passed in via authn_context' do + context 'aal1 is requested' do + let(:authn_context) { Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF } + + # We do not support AAL1. when passed in, we enforce our default AAL value. + # However, we are returning the AAL1 value, which is misleading. + it 'asserts default aal' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'aal2 is requested' do + let(:authn_context) { Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF } + + it 'asserts plain aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'aal2 with phishing-resistant mfa is requested' do + let(:authn_context) { Saml::Idp::Constants::AAL2_PHISHING_RESISTANT_AUTHN_CONTEXT_CLASSREF } + + # we should assert the more specific aal2 value + it 'asserts plain aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'aal2 with hspd12 mfa requested' do + let(:authn_context) { Saml::Idp::Constants::AAL2_HSPD12_AUTHN_CONTEXT_CLASSREF } + + # we should assert the more specific aal2 value + it 'asserts plain aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + # we need to deprecate AAL3 values, as we are not enforcing AAL3. + context 'aal3 requested' do + let(:authn_context) { Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF } + + # when aal3 is requested, we are enforcing aal2 with phishing-resistant mfa. + # we should update to assert that + it 'asserts plain aal3' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'aal3 with hspd12 mfa requested' do + let(:authn_context) { Saml::Idp::Constants::AAL3_HSPD12_AUTHN_CONTEXT_CLASSREF } + + # when aal3 is requested, we are enforcing aal2 with HSPD12 mfa. + # we should update to assert that + it 'asserts plain aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + describe 'when multiple aal values are requested via authn_context' do + # currently, if values are passed in the request, the saml_idp gem only + # returns the first option. + context 'default is first' do + let(:authn_context) do + [ + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ] + end + + it 'asserts the default value' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'aal1 is first' do + let(:authn_context) do + [ + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ] + end + + it 'asserts the aal1 value' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + end + + describe 'ial is passed in via authn_context' do + context 'auth-only is requested' do + let(:authn_context) { [Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF] } + + describe 'no aal is requested via authn_context' do + context 'default_aal is nil' do + it 'asserts default aal' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 1' do + let(:service_provider_aal) { 1 } + + it 'asserts aal1' do + # we do not enforce aal1, we enforce default aal, so this should be updated + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 2' do + let(:service_provider_aal) { 2 } + + it 'asserts aal1' do + # we do not enforce aal1, we enforce default aal, so this should be updated + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 3' do + let(:service_provider_aal) { 3 } + + it 'asserts aal1' do + # we do not enforce aal1, we enforce default aal, so this should be updated + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + end + + context 'identity-proofing is requested' do + let(:authn_context) { [Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF] } + + describe 'no aal is requested via authn_context' do + context 'default_aal is nil' do + it 'asserts default aal' do + # this should be upgraded to AAL2, as we enforce that on an identity-proofing request + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 1' do + let(:service_provider_aal) { 1 } + + it 'asserts aal1' do + # this should be upgraded to AAL2, as we enforce that on an identity-proofing request + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 2' do + let(:service_provider_aal) { 2 } + + it 'asserts base aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 3' do + let(:service_provider_aal) { 3 } + + it 'asserts aal3' do + # we do not enforce aal3, we enforce aal2 with phishing-resistant mfa + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + + describe 'multiple aal values are requested' do + context 'default is first' do + let(:authn_context) do + [ + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ] + end + + # identity proofing enforces aal2, so that is what should be asserted + it 'asserts the default value' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + end + + context 'ialmax is requested' do + let(:options) do + { + authn_context: [ + Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, + ], + authn_context_comparison: 'minimum', + } + end + + context 'a non-verified user' do + # remove any profiles + before do + user.profiles.delete_all + subject.build + end + + describe 'no aal is requested via authn_context' do + context 'default_aal is nil' do + it 'asserts default aal' do + # this is fine, as we have enforced auth-only + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 1' do + let(:service_provider_aal) { 1 } + + it 'asserts aal1' do + # this is fine, as we have enforced auth-only + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 2' do + let(:service_provider_aal) { 2 } + + it 'asserts base aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 3' do + let(:service_provider_aal) { 3 } + + it 'asserts aal3' do + # we do not enforce aal3, we enforce aal2 with phishing-resistant mfa + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + end + + context 'a verified user' do + describe 'no aal is requested via authn_context' do + context 'default_aal is nil' do + it 'asserts default aal' do + # this should be upgraded to AAL2, as we enforce that + # on an identity-proofing request + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 1' do + let(:service_provider_aal) { 1 } + + it 'asserts aal1' do + # this should be upgraded to AAL2, as we enforce that + # on an identity-proofing request + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 2' do + let(:service_provider_aal) { 2 } + + it 'asserts base aal2' do + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + ) + end + end + + context 'default_aal is 3' do + let(:service_provider_aal) { 3 } + + it 'asserts aal3' do + # we do not enforce aal3, we enforce aal2 with phishing-resistant mfa + expect(get_asserted_attribute(user, :aal)).to eq( + Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, + ) + end + end + end + end + end + end + end + def get_asserted_attribute(user, attribute) user.asserted_attributes[attribute][:getter].call(user) end diff --git a/spec/services/usps_in_person_proofing/proofer_spec.rb b/spec/services/usps_in_person_proofing/proofer_spec.rb index 000dac22c58..03e69423712 100644 --- a/spec/services/usps_in_person_proofing/proofer_spec.rb +++ b/spec/services/usps_in_person_proofing/proofer_spec.rb @@ -403,6 +403,7 @@ def expect_facility_fields_to_be_present(facility) 'applicant', unique_id: '123456789', enrollment_code: '123456789', + sponsor_id: '314159265359', ) end @@ -410,13 +411,33 @@ def expect_facility_fields_to_be_present(facility) stub_request_token end + context 'when the user is going through enhanced ipp' do + let(:request_body) do + { + sponsorID: applicant.sponsor_id.to_i, + uniqueID: applicant.unique_id, + enrollmentCode: applicant.enrollment_code, + } + end + let(:faraday_response) { double('faraday_response', body: 'blah blah') } + let(:faraday) { Faraday.new } + before do + allow(Faraday).to receive(:new).and_return(faraday) + allow(Rails.cache).to receive(:read).and_return('some fake token') + end + it 'sends the correct information in the request body' do + expect(faraday).to receive(:post).with( + anything, request_body, + anything + ).and_return(faraday_response) + subject.request_proofing_results(applicant) + end + end + it 'returns failed enrollment information' do stub_request_failed_proofing_results - proofing_results = subject.request_proofing_results( - applicant.unique_id, - applicant.enrollment_code, - ) + proofing_results = subject.request_proofing_results(applicant) expect(proofing_results['status']).to eq 'In-person failed' expect(proofing_results['fraudSuspected']).to eq false end @@ -424,10 +445,7 @@ def expect_facility_fields_to_be_present(facility) it 'returns passed enrollment information' do stub_request_passed_proofing_results - proofing_results = subject.request_proofing_results( - applicant.unique_id, - applicant.enrollment_code, - ) + proofing_results = subject.request_proofing_results(applicant) expect(proofing_results['status']).to eq 'In-person passed' expect(proofing_results['fraudSuspected']).to eq false end @@ -436,10 +454,7 @@ def expect_facility_fields_to_be_present(facility) stub_request_in_progress_proofing_results expect do - subject.request_proofing_results( - applicant.unique_id, - applicant.enrollment_code, - ) + subject.request_proofing_results(applicant) end.to raise_error( an_instance_of(Faraday::BadRequestError). and(having_attributes( @@ -469,10 +484,7 @@ def expect_facility_fields_to_be_present(facility) subject.token proofing_results = nil travel_to(expires_at) do - proofing_results = subject.request_proofing_results( - applicant.unique_id, - applicant.enrollment_code, - ) + proofing_results = subject.request_proofing_results(applicant) end expect(WebMock).to have_requested(:post, "#{root_url}/oauth/authenticate").twice @@ -481,50 +493,4 @@ def expect_facility_fields_to_be_present(facility) end end end - - describe '#request_enrollment_code' do - let(:applicant) do - double( - 'applicant', - unique_id: '123456789', - ) - end - - before(:each) do - stub_request_token - end - - it 'returns enrollment information' do - stub_request_enrollment_code - - enrollment = subject.request_enrollment_code(applicant) - expect(enrollment['enrollmentCode']).to be_present - expect(enrollment['responseMessage']).to be_present - end - - context 'when the auth token is expired' do - expires_at = nil - let(:expires_in) { 15.minutes } - - before do - stub_request_enrollment_code - end - - before(:each) do - subject.retrieve_token! - expires_at = Time.zone.now + expires_in - end - - it 'refreshes the auth token before making the request' do - enrollment = nil - travel_to(expires_at) do - enrollment = subject.request_enrollment_code(applicant) - end - - expect(WebMock).to have_requested(:post, "#{root_url}/oauth/authenticate").twice - expect(enrollment['enrollmentCode']).to be_present - expect(enrollment['responseMessage']).to be_present - end - end - end end diff --git a/spec/support/usps_ipp_helper.rb b/spec/support/usps_ipp_helper.rb index 67b9ab8fff9..03fb458c49b 100644 --- a/spec/support/usps_ipp_helper.rb +++ b/spec/support/usps_ipp_helper.rb @@ -337,21 +337,6 @@ def stub_request_proofing_results_with_invalid_response ) end - def stub_request_enrollment_code - stub_request(:post, %r{/ivs-ippaas-api/IPPRest/resources/rest/requestEnrollmentCode}).to_return( - status: 200, - body: UspsInPersonProofing::Mock::Fixtures.request_enrollment_code_response, - headers: { 'content-type' => 'application/json' }, - ) - end - - def stub_request_enrollment_code_with_forbidden_error - stub_request( - :post, - %r{/ivs-ippaas-api/IPPRest/resources/rest/requestEnrollmentCode}, - ).to_raise(Faraday::ForbiddenError) - end - private # Merges an object into the JSON string of a response's body and returns the updated response diff --git a/spec/views/devise/sessions/new.html.erb_spec.rb b/spec/views/devise/sessions/new.html.erb_spec.rb index 6bfa22d06a5..5e68ebcf6c5 100644 --- a/spec/views/devise/sessions/new.html.erb_spec.rb +++ b/spec/views/devise/sessions/new.html.erb_spec.rb @@ -52,26 +52,19 @@ it 'includes a link to security / privacy page and privacy statement act' do render - expect(rendered). - to have_link( - t('notices.privacy.security_and_privacy_practices'), - href: MarketingSite.security_and_privacy_practices_url, - ) - expect(rendered). - to have_selector( - "a[href='#{MarketingSite.security_and_privacy_practices_url}']\ -[target='_blank'][rel='noopener noreferrer']", - ) - - expect(rendered). - to have_link( - t('notices.privacy.privacy_act_statement'), - href: MarketingSite.privacy_act_statement_url, - ) - expect(rendered).to have_selector( - "a[href='#{MarketingSite.privacy_act_statement_url}']\ -[target='_blank'][rel='noopener noreferrer']", - ) + expect(rendered).to have_link( + t('notices.privacy.security_and_privacy_practices'), + href: policy_redirect_url( + policy: :security_and_privacy_practices, + flow: :sign_in, + step: :sign_in, + ), + ) { |link| link[:target] == '_blank' && link[:rel] == 'noopener noreferrer' } + + expect(rendered).to have_link( + t('notices.privacy.privacy_act_statement'), + href: MarketingSite.privacy_act_statement_url, + ) { |link| link[:target] == '_blank' && link[:rel] == 'noopener noreferrer' } end context 'when SP is present' do diff --git a/spec/views/idv/agreement/show.html.erb_spec.rb b/spec/views/idv/agreement/show.html.erb_spec.rb index 78a43dae781..9e25775d867 100644 --- a/spec/views/idv/agreement/show.html.erb_spec.rb +++ b/spec/views/idv/agreement/show.html.erb_spec.rb @@ -28,7 +28,12 @@ it 'renders a link to the privacy & security page' do expect(rendered).to have_link( t('doc_auth.instructions.learn_more'), - href: policy_redirect_url(flow: :idv, step: :agreement, location: :consent), + href: policy_redirect_url( + policy: :security_and_privacy_practices, + flow: :idv, + step: :agreement, + location: :consent, + ), ) end end