diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c6b8cab759..58b76da6753 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -144,6 +144,56 @@ build-review-image: --build-arg "ARG_CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH}" --build-arg "ARG_CI_COMMIT_SHA=${CI_COMMIT_SHA}" +build-idp-image: + stage: review + needs: [] + interruptible: true + variables: + BRANCH_TAGGING_STRING: '' + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + variables: + BRANCH_TAGGING_STRING: '--destination ${ECR_REGISTRY}/identity-idp/idp:main' + - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE != "merge_request_event" + when: never + tags: + - build-pool + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [''] + script: + - mkdir -p /kaniko/.docker + - echo ${CI_ENVIRONMENT_SLUG} + - echo $CI_ENVIRONMENT_SLUG + - echo $CI_COMMIT_BRANCH + - echo $CI_COMMIT_SHA + - |- + KANIKOCFG="\"credsStore\":\"ecr-login\"" + if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then + KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}" + fi + KANIKOCFG="{ ${KANIKOCFG} }" + echo "${KANIKOCFG}" > /kaniko/.docker/config.json + - >- + /kaniko/executor + --context "${CI_PROJECT_DIR}" + --dockerfile "${CI_PROJECT_DIR}/dockerfiles/idp_prod.Dockerfile" + --destination "${ECR_REGISTRY}/identity-idp/idp:${CI_COMMIT_SHA}" + ${BRANCH_TAGGING_STRING} + --cache-repo="${ECR_REGISTRY}/identity-idp/idp/cache" + --cache-ttl=168h + --cache=true + --compressed-caching=false + --build-arg "http_proxy=${http_proxy}" + --build-arg "https_proxy=${https_proxy}" + --build-arg "no_proxy=${no_proxy}" + --build-arg "ARG_CI_ENVIRONMENT_SLUG=${CI_ENVIRONMENT_SLUG}" + --build-arg "ARG_CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH}" + --build-arg "ARG_CI_COMMIT_SHA=${CI_COMMIT_SHA}" + --build-arg "LARGE_FILES_TOKEN=${LARGE_FILES_TOKEN}" + --build-arg "LARGE_FILES_USER=${LARGE_FILES_USER}" + check_changelog: stage: test variables: diff --git a/Gemfile b/Gemfile index 945644650e8..0a851079286 100644 --- a/Gemfile +++ b/Gemfile @@ -68,7 +68,7 @@ gem 'rqrcode' gem 'ruby-progressbar' gem 'ruby-saml' gem 'safe_target_blank', '>= 1.0.2' -gem 'saml_idp', github: '18F/saml_idp', tag: '0.21.1-18f' +gem 'saml_idp', github: '18F/saml_idp', tag: '0.21.0-18f' gem 'scrypt' gem 'simple_form', '>= 5.0.2' gem 'stringex', require: false diff --git a/Gemfile.lock b/Gemfile.lock index fb9fb6ff581..f1038380a47 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,10 +35,10 @@ GIT GIT remote: https://github.com/18F/saml_idp.git - revision: 85c9a24bd4edab43320d0454e2c0e2fac31ffb90 - tag: 0.21.1-18f + revision: 33275d69f7609e448942d6e3ce5c27779920995f + tag: 0.21.0-18f specs: - saml_idp (0.21.1.pre.18f) + saml_idp (0.21.0.pre.18f) activesupport builder faraday @@ -79,35 +79,35 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3.3) - actionpack (= 7.1.3.3) - activesupport (= 7.1.3.3) + actioncable (7.1.3.4) + actionpack (= 7.1.3.4) + activesupport (= 7.1.3.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.3) - actionpack (= 7.1.3.3) - activejob (= 7.1.3.3) - activerecord (= 7.1.3.3) - activestorage (= 7.1.3.3) - activesupport (= 7.1.3.3) + actionmailbox (7.1.3.4) + actionpack (= 7.1.3.4) + activejob (= 7.1.3.4) + activerecord (= 7.1.3.4) + activestorage (= 7.1.3.4) + activesupport (= 7.1.3.4) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.3.3) - actionpack (= 7.1.3.3) - actionview (= 7.1.3.3) - activejob (= 7.1.3.3) - activesupport (= 7.1.3.3) + actionmailer (7.1.3.4) + actionpack (= 7.1.3.4) + actionview (= 7.1.3.4) + activejob (= 7.1.3.4) + activesupport (= 7.1.3.4) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.3.3) - actionview (= 7.1.3.3) - activesupport (= 7.1.3.3) + actionpack (7.1.3.4) + actionview (= 7.1.3.4) + activesupport (= 7.1.3.4) nokogiri (>= 1.8.5) racc rack (>= 2.2.4) @@ -115,35 +115,35 @@ GEM rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.3) - actionpack (= 7.1.3.3) - activerecord (= 7.1.3.3) - activestorage (= 7.1.3.3) - activesupport (= 7.1.3.3) + actiontext (7.1.3.4) + actionpack (= 7.1.3.4) + activerecord (= 7.1.3.4) + activestorage (= 7.1.3.4) + activesupport (= 7.1.3.4) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.3) - activesupport (= 7.1.3.3) + actionview (7.1.3.4) + activesupport (= 7.1.3.4) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.3.3) - activesupport (= 7.1.3.3) + activejob (7.1.3.4) + activesupport (= 7.1.3.4) globalid (>= 0.3.6) - activemodel (7.1.3.3) - activesupport (= 7.1.3.3) - activerecord (7.1.3.3) - activemodel (= 7.1.3.3) - activesupport (= 7.1.3.3) + activemodel (7.1.3.4) + activesupport (= 7.1.3.4) + activerecord (7.1.3.4) + activemodel (= 7.1.3.4) + activesupport (= 7.1.3.4) timeout (>= 0.4.0) - activestorage (7.1.3.3) - actionpack (= 7.1.3.3) - activejob (= 7.1.3.3) - activerecord (= 7.1.3.3) - activesupport (= 7.1.3.3) + activestorage (7.1.3.4) + actionpack (= 7.1.3.4) + activejob (= 7.1.3.4) + activerecord (= 7.1.3.4) + activesupport (= 7.1.3.4) marcel (~> 1.0) - activesupport (7.1.3.3) + activesupport (7.1.3.4) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -458,7 +458,7 @@ GEM pg (1.5.4) pg_query (4.2.3) google-protobuf (>= 3.22.3) - phonelib (0.8.8) + phonelib (0.8.9) pkcs11 (0.3.4) premailer (1.21.0) addressable @@ -513,20 +513,20 @@ GEM rackup (2.1.0) rack (>= 3) webrick (~> 1.8) - rails (7.1.3.3) - actioncable (= 7.1.3.3) - actionmailbox (= 7.1.3.3) - actionmailer (= 7.1.3.3) - actionpack (= 7.1.3.3) - actiontext (= 7.1.3.3) - actionview (= 7.1.3.3) - activejob (= 7.1.3.3) - activemodel (= 7.1.3.3) - activerecord (= 7.1.3.3) - activestorage (= 7.1.3.3) - activesupport (= 7.1.3.3) + rails (7.1.3.4) + actioncable (= 7.1.3.4) + actionmailbox (= 7.1.3.4) + actionmailer (= 7.1.3.4) + actionpack (= 7.1.3.4) + actiontext (= 7.1.3.4) + actionview (= 7.1.3.4) + activejob (= 7.1.3.4) + activemodel (= 7.1.3.4) + activerecord (= 7.1.3.4) + activestorage (= 7.1.3.4) + activesupport (= 7.1.3.4) bundler (>= 1.15.0) - railties (= 7.1.3.3) + railties (= 7.1.3.4) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -541,9 +541,9 @@ GEM rails-i18n (7.0.6) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.1.3.3) - actionpack (= 7.1.3.3) - activesupport (= 7.1.3.3) + railties (7.1.3.4) + actionpack (= 7.1.3.4) + activesupport (= 7.1.3.4) irb rackup (>= 1.0.0) rake (>= 12.2) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 61a7d444d84..9cf3f52c3e4 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -296,6 +296,8 @@ def move_applicant_to_idv_session end def add_proofing_costs(results) + return if results[:context][:sp_costs_added] + results[:context][:stages].each do |stage, hash| if stage == :resolution # transaction_id comes from ConversationId diff --git a/app/controllers/concerns/two_factor_authenticatable_methods.rb b/app/controllers/concerns/two_factor_authenticatable_methods.rb index 3f322cfa340..809f165f313 100644 --- a/app/controllers/concerns/two_factor_authenticatable_methods.rb +++ b/app/controllers/concerns/two_factor_authenticatable_methods.rb @@ -17,7 +17,14 @@ def handle_valid_verification_for_authentication_context(auth_method:) if IdentityConfig.store.feature_new_device_alert_aggregation_enabled && new_device? if current_user.sign_in_new_device_at.blank? - current_user.update(sign_in_new_device_at: disavowal_event.created_at) + if sign_in_notification_timeframe_expired_event.present? + current_user.update( + sign_in_new_device_at: sign_in_notification_timeframe_expired_event.created_at, + ) + else + current_user.update(sign_in_new_device_at: disavowal_event.created_at) + analytics.sign_in_notification_timeframe_expired_absent + end end UserAlerts::AlertUserAboutNewDevice.send_alert( @@ -87,6 +94,15 @@ def reset_attempt_count_if_user_no_longer_locked_out ) end + def sign_in_notification_timeframe_expired_event + return @sign_in_notification_timeframe_expired_event if defined?( + @sign_in_notification_timeframe_expired_event + ) + @sign_in_notification_timeframe_expired_event = current_user.events.where( + event_type: 'sign_in_notification_timeframe_expired', + ).order(created_at: :desc).limit(1).take + end + def handle_remember_device_preference(remember_device_preference) save_user_opted_remember_device_pref(remember_device_preference) save_remember_device_preference(remember_device_preference) diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb index 9a9de7d9068..f36b53c29b4 100644 --- a/app/controllers/saml_idp_controller.rb +++ b/app/controllers/saml_idp_controller.rb @@ -131,6 +131,7 @@ def capture_analytics requested_ial: requested_ial, request_signed: saml_request.signed?, matching_cert_serial: saml_request.service_provider.matching_cert&.serial&.to_s, + requested_nameid_format: saml_request.name_id_format, ) analytics.saml_auth(**analytics_payload) end diff --git a/app/javascript/packages/phone-input/package.json b/app/javascript/packages/phone-input/package.json index e3c49b40102..20d998f4141 100644 --- a/app/javascript/packages/phone-input/package.json +++ b/app/javascript/packages/phone-input/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "dependencies": { "intl-tel-input": "^17.0.19", - "libphonenumber-js": "^1.11.2" + "libphonenumber-js": "^1.11.3" }, "sideEffects": [ "./index.ts" diff --git a/app/jobs/reports/combined_invoice_supplement_report_v2.rb b/app/jobs/reports/combined_invoice_supplement_report_v2.rb index 2d34b71064f..cc2c52415ef 100644 --- a/app/jobs/reports/combined_invoice_supplement_report_v2.rb +++ b/app/jobs/reports/combined_invoice_supplement_report_v2.rb @@ -44,13 +44,13 @@ def build_csv(iaas, partner_accounts) ) end - by_issuer_profile_age_results = iaas.flat_map do |iaa| - iaa.issuers.flat_map do |issuer| + by_issuer_profile_age_results = partner_accounts.flat_map do |partner_account| + partner_account.issuers.flat_map do |issuer| Db::MonthlySpAuthCount::NewUniqueMonthlyUserCountsByPartner.call( - partner: issuer, # just a label + partner: partner_account.partner, issuers: [issuer], - start_date: iaa.start_date, - end_date: iaa.end_date, + start_date: partner_account.start_date, + end_date: partner_account.end_date, ) end end @@ -103,7 +103,7 @@ def combine_by_iaa_month( 'partner_ial2_new_unique_users_year4', 'partner_ial2_new_unique_users_year5', 'partner_ial2_new_unique_users_year_greater_than_5', - 'partner_ial2_new_unique_users_year_unknown', + 'partner_ial2_new_unique_users_unknown', 'issuer_ial1_total_auth_count', 'issuer_ial2_total_auth_count', @@ -118,7 +118,7 @@ def combine_by_iaa_month( 'issuer_ial2_new_unique_users_year4', 'issuer_ial2_new_unique_users_year5', 'issuer_ial2_new_unique_users_year_greater_than_5', - 'issuer_ial2_new_unique_users_year_unknown', + 'issuer_ial2_new_unique_users_unknown', ] by_issuer_iaa_issuer_year_months.each do |iaa_key, issuer_year_months| issuer_year_months.each do |issuer, year_months_data| @@ -161,7 +161,7 @@ def combine_by_iaa_month( partner_results[:partner_ial2_new_unique_users_year4] || 0, partner_results[:partner_ial2_new_unique_users_year5] || 0, partner_results[:partner_ial2_new_unique_users_year_greater_than_5] || 0, - partner_results[:partner_ial2_new_unique_users_year_unknown] || 0, + partner_results[:partner_ial2_new_unique_users_unknown] || 0, (ial1_total_auth_count = extract(issuer_results, :total_auth_count, ial: 1)), (ial2_total_auth_count = extract(issuer_results, :total_auth_count, ial: 2)), @@ -176,7 +176,7 @@ def combine_by_iaa_month( issuer_profile_age_results[:partner_ial2_new_unique_users_year4] || 0, issuer_profile_age_results[:partner_ial2_new_unique_users_year5] || 0, issuer_profile_age_results[:partner_ial2_new_unique_users_year_greater_than_5] || 0, - issuer_profile_age_results[:partner_ial2_new_unique_users_year_unknown] || 0, + issuer_profile_age_results[:partner_ial2_new_unique_users_unknown] || 0, ] end end diff --git a/app/jobs/resolution_proofing_job.rb b/app/jobs/resolution_proofing_job.rb index d62ff8278e1..afa1092459b 100644 --- a/app/jobs/resolution_proofing_job.rb +++ b/app/jobs/resolution_proofing_job.rb @@ -23,10 +23,9 @@ def perform( should_proof_state_id:, ipp_enrollment_in_progress:, user_id: nil, - service_provider_issuer: nil, # rubocop:disable Lint/UnusedMethodArgument + service_provider_issuer: nil, threatmetrix_session_id: nil, - request_ip: nil, - instant_verify_ab_test_discriminator: nil # rubocop:disable Lint/UnusedMethodArgument + request_ip: nil ) timer = JobHelpers::Timer.new @@ -37,9 +36,12 @@ def perform( symbolize_names: true, ) - applicant_pii = decrypted_args[:applicant_pii] - user = User.find_by(id: user_id) + current_sp = ServiceProvider.find_by(issuer: service_provider_issuer) + + applicant_pii = decrypted_args[:applicant_pii] + applicant_pii[:uuid_prefix] = current_sp&.app_id + applicant_pii[:uuid] = user.uuid callback_log_data = make_vendor_proofing_requests( timer: timer, @@ -77,9 +79,9 @@ def make_vendor_proofing_requests( should_proof_state_id:, ipp_enrollment_in_progress: ) - result = resolution_proofer.proof( + result = progressive_proofer.proof( applicant_pii: applicant_pii, - user_email: user&.confirmed_email_addresses&.first&.email, + user_email: user.confirmed_email_addresses.first.email, threatmetrix_session_id: threatmetrix_session_id, request_ip: request_ip, should_proof_state_id: should_proof_state_id, @@ -102,7 +104,7 @@ def make_vendor_proofing_requests( def log_threatmetrix_info(threatmetrix_result, user) logger_info_hash( name: 'ThreatMetrix', - user_id: user&.uuid, + user_id: user.uuid, threatmetrix_request_id: threatmetrix_result.transaction_id, threatmetrix_success: threatmetrix_result.success?, ) @@ -112,8 +114,8 @@ def logger_info_hash(hash) logger.info(hash.to_json) end - def resolution_proofer - @resolution_proofer ||= Proofing::Resolution::ProgressiveProofer.new + def progressive_proofer + @progressive_proofer ||= Proofing::Resolution::ProgressiveProofer.new end def add_threatmetrix_proofing_component(user_id, threatmetrix_result) diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index da01005b8a1..19333a38638 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -47,25 +47,36 @@ def account_reset_cancel(user_id:, message_id: nil, request_id: nil, **extra) # @identity.idp.previous_event_name Account Reset # @param [String] user_id - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Validates the token used for cancelling an account reset - def account_reset_cancel_token_validation(user_id:, errors: nil, **extra) + def account_reset_cancel_token_validation( + user_id:, + success:, + errors:, + error_details: nil, + **extra + ) track_event( 'Account Reset: cancel token validation', - user_id: user_id, - errors: errors, + user_id:, + success:, + errors:, + error_details:, **extra, ) end # @identity.idp.previous_event_name Account Reset - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] user_id # @param [Integer, nil] account_age_in_days number of days since the account was confirmed # @param [Time] account_confirmed_at date that account creation was confirmed # (rounded) or nil if the account was not confirmed # @param [Hash] mfa_method_counts - # @param [Hash] errors + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # An account has been deleted through the account reset flow def account_reset_delete( success:, @@ -73,30 +84,42 @@ def account_reset_delete( account_age_in_days:, account_confirmed_at:, mfa_method_counts:, - errors: nil, + errors:, + error_details: nil, **extra ) track_event( 'Account Reset: delete', - success: success, - user_id: user_id, - account_age_in_days: account_age_in_days, - account_confirmed_at: account_confirmed_at, - mfa_method_counts: mfa_method_counts, - errors: errors, + success:, + user_id:, + account_age_in_days:, + account_confirmed_at:, + mfa_method_counts:, + errors:, + error_details:, **extra, ) end # @identity.idp.previous_event_name Account Reset # @param [String] user_id - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Validates the granted token for account reset - def account_reset_granted_token_validation(user_id: nil, errors: nil, **extra) + def account_reset_granted_token_validation( + success:, + errors:, + error_details: nil, + user_id: nil, + **extra + ) track_event( 'Account Reset: granted token validation', - user_id: user_id, - errors: errors, + success:, + errors:, + error_details:, + user_id:, **extra, ) end @@ -164,14 +187,16 @@ def add_email_confirmation(user_id:, success: nil, **extra) track_event('Add Email: Email Confirmation', user_id: user_id, success: success, **extra) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Tracks request for adding new emails to an account - def add_email_request(success:, errors:, **extra) + def add_email_request(success:, errors:, error_details: nil, **extra) track_event( 'Add Email Requested', - success: success, - errors: errors, + success:, + errors:, + error_details:, **extra, ) end @@ -191,7 +216,7 @@ def add_phone_setup_visit # @identity.idp.previous_event_name TOTP: User Disabled # Tracks when a user deletes their auth app from account # @param [Boolean] success - # @param [Hash] error_details + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] configuration_id def auth_app_delete_submitted( success:, @@ -210,7 +235,7 @@ def auth_app_delete_submitted( # When a user updates name for auth app # @param [Boolean] success - # @param [Hash] error_details + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] configuration_id # Tracks when user submits a name change for an Auth App configuration def auth_app_update_name_submitted( @@ -274,18 +299,18 @@ def backup_code_regenerate_visit(in_account_creation_flow:, **extra) end # Track user creating new BackupCodeSetupForm, record form submission Hash - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [Hash] mfa_method_counts Hash of MFA method with the number of that method on the account # @param [Number] enabled_mfa_methods_count Number of enabled MFA methods on the account # @param [Boolean] in_account_creation_flow Whether page is visited as part of account creation - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission def backup_code_setup_visit( success:, mfa_method_counts:, enabled_mfa_methods_count:, in_account_creation_flow:, - errors: nil, + errors:, error_details: nil, **extra ) @@ -400,26 +425,30 @@ def email_and_password_auth( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Tracks request for deletion of email address - def email_deletion_request(success:, errors:, **extra) + def email_deletion_request(success:, errors:, error_details: nil, **extra) track_event( 'Email Deletion Requested', - success: success, - errors: errors, + success:, + errors:, + error_details:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Tracks if Email Language is updated - def email_language_updated(success:, errors:, **extra) + def email_language_updated(success:, errors:, error_details: nil, **extra) track_event( 'Email Language: Updated', - success: success, - errors: errors, + success:, + errors:, + error_details:, **extra, ) end @@ -443,8 +472,9 @@ def email_sent(action:, ses_message_id:, email_address_id:, **extra) ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Time, nil] event_created_at timestamp for the event # @param [Time, nil] disavowed_device_last_used_at # @param [String, nil] disavowed_device_user_agent @@ -456,6 +486,7 @@ def email_sent(action:, ses_message_id:, email_address_id:, **extra) def event_disavowal( success:, errors:, + error_details: nil, event_created_at: nil, disavowed_device_last_used_at: nil, disavowed_device_user_agent: nil, @@ -467,21 +498,23 @@ def event_disavowal( ) track_event( 'Event disavowal visited', - success: success, - errors: errors, - event_created_at: event_created_at, - disavowed_device_last_used_at: disavowed_device_last_used_at, - disavowed_device_user_agent: disavowed_device_user_agent, - disavowed_device_last_ip: disavowed_device_last_ip, - event_id: event_id, - event_type: event_type, - event_ip: event_ip, + success:, + errors:, + error_details:, + event_created_at:, + disavowed_device_last_used_at:, + disavowed_device_user_agent:, + disavowed_device_last_ip:, + event_id:, + event_type:, + event_ip:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Time, nil] event_created_at timestamp for the event # @param [Time, nil] disavowed_device_last_used_at # @param [String, nil] disavowed_device_user_agent @@ -493,6 +526,7 @@ def event_disavowal( def event_disavowal_password_reset( success:, errors:, + error_details: nil, event_created_at: nil, disavowed_device_last_used_at: nil, disavowed_device_user_agent: nil, @@ -504,21 +538,23 @@ def event_disavowal_password_reset( ) track_event( 'Event disavowal password reset', - success: success, - errors: errors, - event_created_at: event_created_at, - disavowed_device_last_used_at: disavowed_device_last_used_at, - disavowed_device_user_agent: disavowed_device_user_agent, - disavowed_device_last_ip: disavowed_device_last_ip, - event_id: event_id, - event_type: event_type, - event_ip: event_ip, + success:, + errors:, + error_details:, + event_created_at:, + disavowed_device_last_used_at:, + disavowed_device_user_agent:, + disavowed_device_last_ip:, + event_id:, + event_type:, + event_ip:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Time, nil] event_created_at timestamp for the event # @param [Time, nil] disavowed_device_last_used_at # @param [String, nil] disavowed_device_user_agent @@ -530,6 +566,7 @@ def event_disavowal_password_reset( def event_disavowal_token_invalid( success:, errors:, + error_details: nil, event_created_at: nil, disavowed_device_last_used_at: nil, disavowed_device_user_agent: nil, @@ -541,15 +578,16 @@ def event_disavowal_token_invalid( ) track_event( 'Event disavowal token invalid', - success: success, - errors: errors, - event_created_at: event_created_at, - disavowed_device_last_used_at: disavowed_device_last_used_at, - disavowed_device_user_agent: disavowed_device_user_agent, - disavowed_device_last_ip: disavowed_device_last_ip, - event_id: event_id, - event_type: event_type, - event_ip: event_ip, + success:, + errors:, + error_details:, + event_created_at:, + disavowed_device_last_used_at:, + disavowed_device_user_agent:, + disavowed_device_last_ip:, + event_id:, + event_type:, + event_ip:, **extra, ) end @@ -585,8 +623,8 @@ def forget_all_browsers_visited track_event('Forget All Browsers Visited') end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [String] exception # @param [String] profile_fraud_review_pending_at # The user was passed by manual fraud review @@ -607,8 +645,8 @@ def fraud_review_passed( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [String] exception # @param [String] profile_fraud_review_pending_at # The user was rejected by manual fraud review @@ -662,11 +700,11 @@ def idv_acuant_sdk_loaded( end # rubocop:enable Naming/VariableName,Naming/MethodParameterName - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [Boolean] address_edited # @param [Hash] pii_like_keypaths - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # User submitted an idv address def idv_address_submitted( success:, @@ -1056,8 +1094,9 @@ def idv_doc_auth_ssn_visited(**extra) track_event('IdV: doc auth ssn visited', **extra) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] submit_attempts (previously called "attempts") # @param [Integer] remaining_submit_attempts (previously called "remaining_attempts") # @param [String] user_id @@ -1072,6 +1111,7 @@ def idv_doc_auth_submitted_image_upload_form( remaining_submit_attempts:, flow_path:, liveness_checking_required:, + error_details: nil, submit_attempts: nil, user_id: nil, front_image_fingerprint: nil, @@ -1080,21 +1120,22 @@ def idv_doc_auth_submitted_image_upload_form( ) track_event( 'IdV: doc auth image upload form submitted', - success: success, - errors: errors, - submit_attempts: submit_attempts, - remaining_submit_attempts: remaining_submit_attempts, - user_id: user_id, - flow_path: flow_path, - front_image_fingerprint: front_image_fingerprint, - back_image_fingerprint: back_image_fingerprint, - liveness_checking_required: liveness_checking_required, + success:, + errors:, + error_details:, + submit_attempts:, + remaining_submit_attempts:, + user_id:, + flow_path:, + front_image_fingerprint:, + back_image_fingerprint:, + liveness_checking_required:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [String] exception # @param [Boolean] billed # @param [String] doc_auth_result @@ -1222,8 +1263,9 @@ def idv_doc_auth_submitted_image_upload_vendor( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] user_id # @param [Integer] remaining_submit_attempts (previously called "remaining_attempts") # @param [Hash] pii_like_keypaths @@ -1240,6 +1282,7 @@ def idv_doc_auth_submitted_pii_validation( pii_like_keypaths:, flow_path:, liveness_checking_required:, + error_details: nil, user_id: nil, front_image_fingerprint: nil, back_image_fingerprint: nil, @@ -1248,16 +1291,17 @@ def idv_doc_auth_submitted_pii_validation( ) track_event( 'IdV: doc auth image upload vendor pii validation', - success: success, - errors: errors, - user_id: user_id, - remaining_submit_attempts: remaining_submit_attempts, - pii_like_keypaths: pii_like_keypaths, - flow_path: flow_path, - front_image_fingerprint: front_image_fingerprint, - back_image_fingerprint: back_image_fingerprint, - classification_info: classification_info, - liveness_checking_required: liveness_checking_required, + success:, + errors:, + error_details:, + user_id:, + remaining_submit_attempts:, + pii_like_keypaths:, + flow_path:, + front_image_fingerprint:, + back_image_fingerprint:, + classification_info:, + liveness_checking_required:, **extra, ) end @@ -1788,9 +1832,9 @@ def idv_in_person_locations_request_failure( ) end - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [Integer] result_total - # @param [String] errors + # @param [Hash] errors Errors resulting from form validation # @param [String] exception_class # @param [String] exception_message # @param [Integer] response_status_code @@ -1865,27 +1909,30 @@ def idv_in_person_proofing_address_visited( # @param [String] flow_path # @param [String] step # @param [String] analytics_id - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Boolean] same_address_as_id # User clicked cancel on update state id page def idv_in_person_proofing_cancel_update_state_id( + success:, + errors:, flow_path: nil, step: nil, analytics_id: nil, - success: nil, - errors: nil, + error_details: nil, same_address_as_id: nil, **extra ) track_event( 'IdV: in person proofing cancel_update_state_id submitted', - flow_path: flow_path, - step: step, - analytics_id: analytics_id, - success: success, - errors: errors, - same_address_as_id: same_address_as_id, + flow_path:, + step:, + analytics_id:, + success:, + errors:, + error_details:, + same_address_as_id:, **extra, ) end @@ -1962,27 +2009,30 @@ def idv_in_person_proofing_nontransliterable_characters_submitted( # @param [String] flow_path # @param [String] step # @param [String] analytics_id - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Boolean] same_address_as_id # User submitted state id on redo state id page def idv_in_person_proofing_redo_state_id_submitted( + success:, + errors:, + error_details: nil, flow_path: nil, step: nil, analytics_id: nil, - success: nil, - errors: nil, same_address_as_id: nil, **extra ) track_event( 'IdV: in person proofing redo_state_id submitted', - flow_path: flow_path, - step: step, - analytics_id: analytics_id, - success: success, - errors: errors, - same_address_as_id: same_address_as_id, + flow_path:, + step:, + analytics_id:, + success:, + errors:, + error_details:, + same_address_as_id:, **extra, ) end @@ -1994,30 +2044,33 @@ def idv_in_person_proofing_residential_address_submitted(**extra) # @param [String] flow_path # @param [String] step # @param [String] analytics_id - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Boolean, nil] same_address_as_id # @param [Boolean] opted_in_to_in_person_proofing User opted into in person proofing # User submitted state id def idv_in_person_proofing_state_id_submitted( + success:, + errors:, + error_details: nil, flow_path: nil, step: nil, analytics_id: nil, - success: nil, - errors: nil, same_address_as_id: nil, opted_in_to_in_person_proofing: nil, **extra ) track_event( 'IdV: in person proofing state_id submitted', - flow_path: flow_path, - step: step, - analytics_id: analytics_id, - success: success, - errors: errors, - same_address_as_id: same_address_as_id, - opted_in_to_in_person_proofing: opted_in_to_in_person_proofing, + flow_path:, + step:, + analytics_id:, + success:, + errors:, + error_details:, + same_address_as_id:, + opted_in_to_in_person_proofing:, **extra, ) end @@ -2721,8 +2774,9 @@ def idv_personal_key_visited( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param ["sms", "voice"] otp_delivery_preference # @param [Idv::ProofingComponentsLogging] proofing_components User's current proofing components # @param [String,nil] active_profile_idv_level ID verification level of user's active profile. @@ -2732,6 +2786,7 @@ def idv_phone_confirmation_form_submitted( success:, otp_delivery_preference:, errors:, + error_details: nil, proofing_components: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, @@ -2739,12 +2794,13 @@ def idv_phone_confirmation_form_submitted( ) track_event( 'IdV: phone confirmation form', - success: success, - errors: errors, - otp_delivery_preference: otp_delivery_preference, - proofing_components: proofing_components, - active_profile_idv_level: active_profile_idv_level, - pending_profile_idv_level: pending_profile_idv_level, + success:, + errors:, + error_details:, + otp_delivery_preference:, + proofing_components:, + active_profile_idv_level:, + pending_profile_idv_level:, **extra, ) end @@ -2806,8 +2862,9 @@ def idv_phone_confirmation_otp_rate_limit_sends( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param ["sms","voice"] otp_delivery_preference which channel the OTP was delivered by # @param [String] country_code country code of phone number # @param [String] area_code area code of phone number @@ -2828,6 +2885,7 @@ def idv_phone_confirmation_otp_resent( rate_limit_exceeded:, telephony_response:, phone_fingerprint:, + error_details: nil, proofing_components: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, @@ -2836,24 +2894,26 @@ def idv_phone_confirmation_otp_resent( ) track_event( 'IdV: phone confirmation otp resent', - success: success, - errors: errors, - otp_delivery_preference: otp_delivery_preference, - country_code: country_code, - area_code: area_code, - rate_limit_exceeded: rate_limit_exceeded, - telephony_response: telephony_response, - phone_fingerprint: phone_fingerprint, - proofing_components: proofing_components, - active_profile_idv_level: active_profile_idv_level, - pending_profile_idv_level: pending_profile_idv_level, - ab_tests: ab_tests, + success:, + errors:, + error_details:, + otp_delivery_preference:, + country_code:, + area_code:, + rate_limit_exceeded:, + telephony_response:, + phone_fingerprint:, + proofing_components:, + active_profile_idv_level:, + pending_profile_idv_level:, + ab_tests:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param ["sms","voice"] otp_delivery_preference which channel the OTP was delivered by # @param [String] country_code country code of phone number # @param [String] area_code area code of phone number @@ -2875,6 +2935,7 @@ def idv_phone_confirmation_otp_sent( phone_fingerprint:, telephony_response:, adapter:, + error_details: nil, proofing_components: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, @@ -2882,24 +2943,26 @@ def idv_phone_confirmation_otp_sent( ) track_event( 'IdV: phone confirmation otp sent', - success: success, - errors: errors, - otp_delivery_preference: otp_delivery_preference, - country_code: country_code, - area_code: area_code, - rate_limit_exceeded: rate_limit_exceeded, - phone_fingerprint: phone_fingerprint, - telephony_response: telephony_response, - adapter: adapter, - proofing_components: proofing_components, - active_profile_idv_level: active_profile_idv_level, - pending_profile_idv_level: pending_profile_idv_level, + success:, + errors:, + error_details:, + otp_delivery_preference:, + country_code:, + area_code:, + rate_limit_exceeded:, + phone_fingerprint:, + telephony_response:, + adapter:, + proofing_components:, + active_profile_idv_level:, + pending_profile_idv_level:, **extra, ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Boolean] code_expired if the one-time code expired # @param [Boolean] code_matches # @param [:sms,:voice] otp_delivery_preference @@ -2917,6 +2980,7 @@ def idv_phone_confirmation_otp_submitted( otp_delivery_preference:, second_factor_attempts_count:, second_factor_locked_at:, + error_details: nil, proofing_components: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, @@ -2924,16 +2988,17 @@ def idv_phone_confirmation_otp_submitted( ) track_event( 'IdV: phone confirmation otp submitted', - success: success, - errors: errors, - code_expired: code_expired, - code_matches: code_matches, - otp_delivery_preference: otp_delivery_preference, - second_factor_attempts_count: second_factor_attempts_count, - second_factor_locked_at: second_factor_locked_at, - proofing_components: proofing_components, - active_profile_idv_level: active_profile_idv_level, - pending_profile_idv_level: pending_profile_idv_level, + success:, + errors:, + error_details:, + code_expired:, + code_matches:, + otp_delivery_preference:, + second_factor_attempts_count:, + second_factor_locked_at:, + proofing_components:, + active_profile_idv_level:, + pending_profile_idv_level:, **extra, ) end @@ -2957,8 +3022,9 @@ def idv_phone_confirmation_otp_visit( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Idv::ProofingComponentsLogging] proofing_components User's current proofing components # @param [String,nil] active_profile_idv_level ID verification level of user's active profile. # @param [String,nil] pending_profile_idv_level ID verification level of user's pending profile. @@ -2966,6 +3032,7 @@ def idv_phone_confirmation_otp_visit( def idv_phone_confirmation_vendor_submitted( success:, errors:, + error_details: nil, proofing_components: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, @@ -2973,11 +3040,12 @@ def idv_phone_confirmation_vendor_submitted( ) track_event( 'IdV: phone confirmation vendor', - success: success, - errors: errors, - proofing_components: proofing_components, - active_profile_idv_level: active_profile_idv_level, - pending_profile_idv_level: pending_profile_idv_level, + success:, + errors:, + error_details:, + proofing_components:, + active_profile_idv_level:, + pending_profile_idv_level:, **extra, ) end @@ -3431,8 +3499,9 @@ def idv_usps_auth_token_refresh_job_started(**extra) # @identity.idp.previous_event_name Account verification submitted # @identity.idp.previous_event_name IdV: GPO verification submitted - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Hash] pii_like_keypaths # @param [DateTime] enqueued_at When was this letter enqueued # @param [Integer] which_letter Sorted by enqueue time, which letter had this code @@ -3454,19 +3523,21 @@ def idv_verify_by_mail_enter_code_submitted( submit_attempts:, pending_in_person_enrollment:, fraud_check_failed:, + error_details: nil, **extra ) track_event( 'IdV: enter verify by mail code submitted', - success: success, - errors: errors, - pii_like_keypaths: pii_like_keypaths, - enqueued_at: enqueued_at, - which_letter: which_letter, - letter_count: letter_count, - submit_attempts: submit_attempts, - pending_in_person_enrollment: pending_in_person_enrollment, - fraud_check_failed: fraud_check_failed, + success:, + errors:, + error_details:, + pii_like_keypaths:, + enqueued_at:, + which_letter:, + letter_count:, + submit_attempts:, + pending_in_person_enrollment:, + fraud_check_failed:, **extra, ) end @@ -3581,15 +3652,15 @@ def invalid_authenticity_token( ) end - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] client_id # @param [Boolean] client_id_parameter_present # @param [Boolean] id_token_hint_parameter_present # @param [Boolean] sp_initiated # @param [Boolean] oidc # @param [Boolean] saml_request_valid - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] method # Logout Initiated def logout_initiated( @@ -3623,7 +3694,7 @@ def logout_initiated( # @param [Boolean] success Whether authentication was successful # @param [Hash] errors Authentication error reasons, if unsuccessful - # @param [Hash] error_details Details for error that occurred in unsuccessful submission + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param ["authentication","reauthentication","confirmation"] context User session context # @param [Boolean] new_device Whether the user is authenticating from a new device # @param [String] multi_factor_auth_method Authentication method used @@ -3860,15 +3931,17 @@ def multi_factor_auth_max_sends end # Multi factor selected from auth options list - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] selection - def multi_factor_auth_option_list(success:, errors:, selection:, **extra) + def multi_factor_auth_option_list(success:, errors:, selection:, error_details: nil, **extra) track_event( 'Multi-Factor Authentication: option list', - success: success, - errors: errors, - selection: selection, + success:, + errors:, + error_details:, + selection:, **extra, ) end @@ -3879,33 +3952,38 @@ def multi_factor_auth_option_list_visit end # Multi factor auth phone setup - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] otp_delivery_preference # @param [String] area_code # @param [String] carrier # @param [String] country_code # @param [String] phone_type # @param [Hash] types - def multi_factor_auth_phone_setup(success:, - errors:, - otp_delivery_preference:, - area_code:, - carrier:, - country_code:, - phone_type:, - types:, - **extra) + def multi_factor_auth_phone_setup( + success:, + errors:, + otp_delivery_preference:, + area_code:, + carrier:, + country_code:, + phone_type:, + types:, + error_details: nil, + **extra + ) track_event( 'Multi-Factor Authentication: phone setup', - success: success, - errors: errors, - otp_delivery_preference: otp_delivery_preference, - area_code: area_code, - carrier: carrier, - country_code: country_code, - phone_type: phone_type, - types: types, + success:, + errors:, + error_details:, + otp_delivery_preference:, + area_code:, + carrier:, + country_code:, + phone_type:, + types:, **extra, ) end @@ -3913,6 +3991,7 @@ def multi_factor_auth_phone_setup(success:, # Tracks when a user sets up a multi factor auth method # @param [Boolean] success Whether authenticator setup was successful # @param [Hash] errors Authenticator setup error reasons, if unsuccessful + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] multi_factor_auth_method # @param [Boolean] in_account_creation_flow whether user is going through account creation flow # @param [integer] enabled_mfa_methods_count @@ -3922,15 +4001,17 @@ def multi_factor_auth_setup( enabled_mfa_methods_count:, in_account_creation_flow:, errors: nil, + error_details: nil, **extra ) track_event( 'Multi-Factor Authentication Setup', - success: success, - errors: errors, - multi_factor_auth_method: multi_factor_auth_method, - in_account_creation_flow: in_account_creation_flow, - enabled_mfa_methods_count: enabled_mfa_methods_count, + success:, + errors:, + error_details:, + multi_factor_auth_method:, + in_account_creation_flow:, + enabled_mfa_methods_count:, **extra, ) end @@ -3941,28 +4022,28 @@ def no_js_detect_stylesheet_loaded(location:, **extra) track_event(:no_js_detect_stylesheet_loaded, location:, **extra) end - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] client_id # @param [Boolean] client_id_parameter_present # @param [Boolean] id_token_hint_parameter_present # @param [Boolean] sp_initiated # @param [Boolean] oidc # @param [Boolean] saml_request_valid - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] method # @param [String] original_method Method of referring request # OIDC Logout Requested def oidc_logout_requested( - success: nil, + success:, + errors:, + error_details: nil, client_id: nil, sp_initiated: nil, oidc: nil, client_id_parameter_present: nil, id_token_hint_parameter_present: nil, saml_request_valid: nil, - errors: nil, - error_details: nil, method: nil, original_method: nil, **extra @@ -3984,15 +4065,15 @@ def oidc_logout_requested( ) end - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] client_id # @param [Boolean] client_id_parameter_present # @param [Boolean] id_token_hint_parameter_present # @param [Boolean] sp_initiated # @param [Boolean] oidc # @param [Boolean] saml_request_valid - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] method # OIDC Logout Submitted def oidc_logout_submitted( @@ -4024,26 +4105,26 @@ def oidc_logout_submitted( ) end - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] client_id # @param [Boolean] client_id_parameter_present # @param [Boolean] id_token_hint_parameter_present # @param [Boolean] sp_initiated # @param [Boolean] oidc # @param [Boolean] saml_request_valid - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] method # OIDC Logout Visited def oidc_logout_visited( - success: nil, + success:, + errors:, client_id: nil, sp_initiated: nil, oidc: nil, client_id_parameter_present: nil, id_token_hint_parameter_present: nil, saml_request_valid: nil, - errors: nil, error_details: nil, method: nil, **extra @@ -4087,17 +4168,19 @@ def openid_connect_authorization_handoff( end # Tracks when an openid connect bearer token authentication request is made - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [Integer] ial # @param [String] client_id Service Provider issuer - # @param [Hash] errors - def openid_connect_bearer_token(success:, ial:, client_id:, errors:, **extra) + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission + def openid_connect_bearer_token(success:, ial:, client_id:, errors:, error_details: nil, **extra) track_event( 'OpenID Connect: bearer token authentication', - success: success, - ial: ial, - client_id: client_id, - errors: errors, + success:, + ial:, + client_id:, + errors:, + error_details:, **extra, ) end @@ -4105,6 +4188,7 @@ def openid_connect_bearer_token(success:, ial:, client_id:, errors:, **extra) # Tracks when openid authorization request is made # @param [Boolean] success Whether form validations were succcessful # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] prompt OIDC prompt parameter # @param [Boolean] allow_prompt_login Whether service provider is configured to allow prompt=login # @param [Boolean] code_challenge_present Whether code challenge is present @@ -4132,12 +4216,14 @@ def openid_connect_request_authorization( vtr_param:, unauthorized_scope:, user_fully_authenticated:, + error_details: nil, **extra ) track_event( 'OpenID Connect: authorization request', success:, errors:, + error_details:, prompt:, allow_prompt_login:, code_challenge_present:, @@ -4175,6 +4261,7 @@ def openid_connect_token(client_id:, user_id:, code_digest:, expires_in:, ial:, # Tracks when user makes an otp delivery selection # @param [Boolean] success Whether the form was submitted successfully. # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param ["authentication","reauthentication","confirmation"] context User session context # @param [String] otp_delivery_preference (sms or voice) # @param [Boolean] resend True if the user re-requested a code @@ -4188,12 +4275,14 @@ def otp_delivery_selection( resend:, country_code:, area_code:, + error_details: nil, **extra ) track_event( 'OTP: Delivery Selection', success:, errors:, + error_details:, context:, otp_delivery_preference:, resend:, @@ -4220,20 +4309,37 @@ def otp_phone_validation_failed(error:, message:, context:, country:, **extra) ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # The user updated their password - def password_changed(success:, errors:, **extra) - track_event('Password Changed', success: success, errors: errors, **extra) + def password_changed(success:, errors:, error_details: nil, **extra) + track_event('Password Changed', success:, errors:, error_details:, **extra) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] user_id UUID of the user # @param [Boolean] request_id_present Whether request_id URL parameter is present # The user added a password after verifying their email for account creation - def password_creation(success:, errors:, user_id:, request_id_present:, **extra) - track_event('Password Creation', success:, errors:, user_id:, request_id_present:, **extra) + def password_creation( + success:, + errors:, + user_id:, + request_id_present:, + error_details: nil, + **extra + ) + track_event( + 'Password Creation', + success:, + errors:, + error_details:, + user_id:, + request_id_present:, + **extra, + ) end # The user got their password incorrect the max number of times, their session was terminated @@ -4241,13 +4347,13 @@ def password_max_attempts track_event('Password Max Attempts Reached') end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [Boolean, nil] confirmed if the account the reset is being requested for has a # confirmed email # @param [Boolean, nil] active_profile if the account the reset is being requested for has an # active proofed profile - # @param [Hash] error_details Details for error that occurred in unsuccessful submission + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # The user entered an email address to request a password reset def password_reset_email( success:, @@ -4268,15 +4374,15 @@ def password_reset_email( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [Boolean] profile_deactivated if the active profile for the account was deactivated # (the user will need to use their personal key to reactivate their profile) # @param [Boolean] pending_profile_invalidated Whether a pending profile was invalidated as a # result of the password reset # @param [String] pending_profile_pending_reasons Comma-separated list of the pending states # associated with the associated profile. - # @param [Hash] error_details Details for error that occurred in unsuccessful submission + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # The user changed the password for their account via the password reset flow def password_reset_password( success:, @@ -4299,16 +4405,18 @@ def password_reset_password( ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] user_id UUID of the user to receive password token # A password token has been sent for user - def password_reset_token(success:, errors:, user_id:, **extra) + def password_reset_token(success:, errors:, user_id:, error_details: nil, **extra) track_event( 'Password Reset: Token Submitted', - success: success, - errors: errors, - user_id: user_id, + success:, + errors:, + error_details:, + user_id:, **extra, ) end @@ -4328,14 +4436,16 @@ def pending_account_reset_visited track_event('Pending account reset visited') end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Alert user if a personal key was used to sign in - def personal_key_alert_about_sign_in(success:, errors:, **extra) + def personal_key_alert_about_sign_in(success:, errors:, error_details: nil, **extra) track_event( 'Personal key: Alert user about sign in', - success: success, - errors: errors, + success:, + errors:, + error_details:, **extra, ) end @@ -4345,16 +4455,24 @@ def personal_key_reactivation track_event('Personal key reactivation: Account reactivated with personal key') end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Hash] pii_like_keypaths # Personal key form submitted - def personal_key_reactivation_submitted(success:, errors:, pii_like_keypaths:, **extra) + def personal_key_reactivation_submitted( + success:, + errors:, + pii_like_keypaths:, + error_details: nil, + **extra + ) track_event( 'Personal key reactivation: Personal key form submitted', - success: success, - errors: errors, - pii_like_keypaths: pii_like_keypaths, + success:, + errors:, + error_details:, + pii_like_keypaths:, **extra, ) end @@ -4374,8 +4492,9 @@ def personal_key_viewed(personal_key_present:, **extra) ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] delivery_preference # @param [Integer] phone_configuration_id # @param [Boolean] make_default_number @@ -4386,15 +4505,17 @@ def phone_change_submitted( delivery_preference:, phone_configuration_id:, make_default_number:, + error_details: nil, **extra ) track_event( 'Phone Number Change: Form submitted', - success: success, - errors: errors, - delivery_preference: delivery_preference, - phone_configuration_id: phone_configuration_id, - make_default_number: make_default_number, + success:, + errors:, + error_details:, + delivery_preference:, + phone_configuration_id:, + make_default_number:, **extra, ) end @@ -4426,7 +4547,7 @@ def phone_input_country_changed(country_code:, **extra) # @identity.idp.previous_event_name PIV CAC disabled # @identity.idp.previous_event_name piv_cac_disabled # @param [Boolean] success - # @param [Hash] error_details + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] configuration_id # Tracks when user attempts to delete a PIV/CAC configuraton def piv_cac_delete_submitted( @@ -4445,8 +4566,8 @@ def piv_cac_delete_submitted( end # @identity.idp.previous_event_name PIV/CAC login - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation # @param [String,nil] key_id # tracks piv cac login event def piv_cac_login(success:, errors:, key_id:, **extra) @@ -4490,8 +4611,8 @@ def piv_cac_setup_visited(in_account_creation_flow:, **extra) ) end - # @param [Boolean] success - # @param [Hash] error_details + # @param [Boolean] success Whether form validation was successful + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] configuration_id # Tracks when user submits a name change for a PIV/CAC configuraton def piv_cac_update_name_submitted( @@ -4777,13 +4898,15 @@ def risc_security_event_pushed( end # Tracks when rules of use is submitted with a success or failure - # @param [Boolean] success - # @param [Hash] errors - def rules_of_use_submitted(success: nil, errors: nil, **extra) + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission + def rules_of_use_submitted(success:, errors:, error_details: nil, **extra) track_event( 'Rules of Use Submitted', - success: success, - errors: errors, + success:, + errors:, + error_details:, **extra, ) end @@ -4794,9 +4917,11 @@ def rules_of_use_visit end # Record SAML authentication payload Hash - # @param [Boolean] success - # @param [Hash] errors - # @param [String] nameid_format + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission + # @param [String] nameid_format The NameID format sent in the response + # @param [String] requested_nameid_format The NameID format requested # @param [Array] authn_context # @param [String] authn_context_comparison # @param [String] service_provider @@ -4810,6 +4935,7 @@ def saml_auth( success:, errors:, nameid_format:, + requested_nameid_format:, authn_context:, authn_context_comparison:, service_provider:, @@ -4819,22 +4945,25 @@ def saml_auth( requested_ial:, request_signed:, matching_cert_serial:, + error_details: nil, **extra ) track_event( 'SAML Auth', - success: success, - errors: errors, - nameid_format: nameid_format, - authn_context: authn_context, - authn_context_comparison: authn_context_comparison, - service_provider: service_provider, - endpoint: endpoint, - idv: idv, - finish_profile: finish_profile, - requested_ial: requested_ial, - request_signed: request_signed, - matching_cert_serial: matching_cert_serial, + success:, + errors:, + error_details:, + nameid_format:, + requested_nameid_format:, + authn_context:, + authn_context_comparison:, + service_provider:, + endpoint:, + idv:, + finish_profile:, + requested_ial:, + request_signed:, + matching_cert_serial:, **extra, ) end @@ -4887,16 +5016,18 @@ def second_mfa_reminder_visit end # Tracks when security event is received - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [String] error_code - # @param [Hash] errors + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] jti # @param [String] user_id # @param [String] client_id def security_event_received( success:, + errors:, error_code: nil, - errors: nil, + error_details: nil, jti: nil, user_id: nil, client_id: nil, @@ -4904,12 +5035,13 @@ def security_event_received( ) track_event( 'RISC: Security event received', - success: success, - error_code: error_code, - errors: errors, - jti: jti, - user_id: user_id, - client_id: client_id, + success:, + errors:, + error_details:, + error_code:, + jti:, + user_id:, + client_id:, **extra, ) end @@ -4929,6 +5061,11 @@ def session_total_duration_timeout track_event('User Maximum Session Length Exceeded') end + # User events missing sign_in_notification_timeframe_expired + def sign_in_notification_timeframe_expired_absent + track_event(:sign_in_notification_timeframe_expired_absent) + end + # @param [String] flash # tracks when a user visits the sign in page def sign_in_page_visit(flash:, **extra) @@ -5153,15 +5290,17 @@ def user_password_compromised_visited(**extra) ) end - # @param [Boolean] success - # @param [Hash] errors + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [Integer] enabled_mfa_methods_count # @param [Integer] selected_mfa_count # @param ['voice', 'auth_app'] selection # Tracks when the the user has selected and submitted MFA auth methods on user registration def user_registration_2fa_setup( success:, - errors: nil, + errors:, + error_details: nil, selected_mfa_count: nil, enabled_mfa_methods_count: nil, selection: nil, @@ -5170,11 +5309,12 @@ def user_registration_2fa_setup( track_event( 'User Registration: 2FA Setup', { - success: success, - errors: errors, - selected_mfa_count: selected_mfa_count, - enabled_mfa_methods_count: enabled_mfa_methods_count, - selection: selection, + success:, + errors:, + error_details:, + selected_mfa_count:, + enabled_mfa_methods_count:, + selection:, **extra, }.compact, ) @@ -5274,10 +5414,10 @@ def user_registration_complete( end # Tracks when user submits registration email - # @param [Boolean] success + # @param [Boolean] success Whether form validation was successful # @param [Boolean] rate_limited - # @param [Hash] errors - # @param [Hash] error_details + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] user_id # @param [Boolean] email_already_exists # @param [String] domain_name @@ -5307,9 +5447,9 @@ def user_registration_email( end # Tracks when user confirms registration email - # @param [Boolean] success - # @param [Hash] errors - # @param [Hash] error_details + # @param [Boolean] success Whether form validation was successful + # @param [Hash] errors Errors resulting from form validation + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # @param [String] user_id def user_registration_email_confirmation( success:, @@ -5485,7 +5625,7 @@ def vendor_outage( # @param [Boolean] success Whether the submission was successful # @param [Integer] configuration_id Database ID for the configuration # @param [Boolean] platform_authenticator Whether the configuration was a platform authenticator - # @param [Hash] error_details Details for error that occurred in unsuccessful submission + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Tracks when user attempts to delete a WebAuthn configuration # @identity.idp.previous_event_name WebAuthn Deleted def webauthn_delete_submitted( @@ -5534,7 +5674,7 @@ def webauthn_setup_visit(platform_authenticator:, enabled_mfa_methods_count:, ** # @param [Boolean] success Whether the submission was successful # @param [Integer] configuration_id Database ID for the configuration # @param [Boolean] platform_authenticator Whether the configuration was a platform authenticator - # @param [Hash] error_details Details for error that occurred in unsuccessful submission + # @param [Hash] error_details Details for errors that occurred in unsuccessful submission # Tracks when user submits a name change for a WebAuthn configuration def webauthn_update_name_submitted( success:, diff --git a/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb b/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb index 65be782b6de..eda78616f76 100644 --- a/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb +++ b/app/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner.rb @@ -5,6 +5,8 @@ module MonthlySpAuthCount module NewUniqueMonthlyUserCountsByPartner extend Reports::QueryHelpers + UserVerifiedKey = Data.define(:user_id, :profile_verified_at, :profile_age).freeze + module_function # @param [String] partner label for billing (Partner requesting agency) @@ -43,19 +45,21 @@ def call(partner:, issuers:, start_date:, end_date:) ) do Reports::BaseReport.transaction_with_timeout do ActiveRecord::Base.connection.execute(query).each do |row| - user_id = row['user_id'] year_month = row['year_month'] profile_age = row['profile_age'] + user_id = row['user_id'] + profile_verified_at = row['profile_verified_at'] - year_month_to_users_to_profile_age[year_month][user_id] = profile_age + user_unique_id = UserVerifiedKey.new(user_id:, profile_verified_at:, profile_age:) + + year_month_to_users_to_profile_age[year_month][user_unique_id] = profile_age end end end end - rows = [] - prev_seen_users = Set.new + prev_seen_user_proofed_events = Set.new issuers_set = issuers.to_set year_months = year_month_to_users_to_profile_age.keys.sort @@ -63,11 +67,12 @@ def call(partner:, issuers:, start_date:, end_date:) year_months.each do |year_month| users_to_profile_age = year_month_to_users_to_profile_age[year_month] - this_month_users = users_to_profile_age.keys.to_set - new_unique_users = this_month_users - prev_seen_users + this_month_user_proofed_events = users_to_profile_age.keys.to_set + new_unique_user_proofed_events = this_month_user_proofed_events - + prev_seen_user_proofed_events - profile_age_counts = new_unique_users.group_by do |user_id| - age = users_to_profile_age[user_id] + profile_age_counts = new_unique_user_proofed_events.group_by do |user_unique_id| + age = users_to_profile_age[user_unique_id] if age.nil? || age < 0 :unknown elsif age > 4 @@ -77,7 +82,7 @@ def call(partner:, issuers:, start_date:, end_date:) end end.tap { |counts| counts.default = [] } - prev_seen_users |= this_month_users + prev_seen_user_proofed_events |= this_month_user_proofed_events rows << { partner: partner, @@ -85,8 +90,8 @@ def call(partner:, issuers:, start_date:, end_date:) year_month: year_month, iaa_start_date: date_range.begin.to_s, iaa_end_date: date_range.end.to_s, - unique_users: this_month_users.count, - new_unique_users: new_unique_users.count, + unique_user_proofed_events: this_month_user_proofed_events.count, + new_unique_user_proofed_events: new_unique_user_proofed_events.count, partner_ial2_new_unique_users_year1: profile_age_counts[0].count, partner_ial2_new_unique_users_year2: profile_age_counts[1].count, partner_ial2_new_unique_users_year3: profile_age_counts[2].count, @@ -105,7 +110,7 @@ def call(partner:, issuers:, start_date:, end_date:) # the first and last may be partial months # @return [Array] def build_queries(issuers:, months:) - months.map do |month_range| + months.map do |month_range| # rubocop:disable Metrics/BlockLength params = { range_start: month_range.begin, range_end: month_range.end, @@ -117,10 +122,12 @@ def build_queries(issuers:, months:) SELECT subq.user_id AS user_id , %{year_month} AS year_month - , MIN(subq.profile_age) AS profile_age + , subq.profile_verified_at + , subq.profile_age FROM ( SELECT sp_return_logs.user_id + , sp_return_logs.profile_verified_at , DATE_PART('year', AGE(sp_return_logs.returned_at, sp_return_logs.profile_verified_at)) AS profile_age FROM sp_return_logs WHERE @@ -131,6 +138,8 @@ def build_queries(issuers:, months:) ) subq GROUP BY subq.user_id + , subq.profile_verified_at + , subq.profile_age SQL end end diff --git a/app/services/proofing/resolution/progressive_proofer.rb b/app/services/proofing/resolution/progressive_proofer.rb index a2a339bb1f0..031c445e6ad 100644 --- a/app/services/proofing/resolution/progressive_proofer.rb +++ b/app/services/proofing/resolution/progressive_proofer.rb @@ -8,6 +8,13 @@ module Resolution # 2. The user has only provided one address for their residential and identity document # address or separate residential and identity document addresses class ProgressiveProofer + attr_reader :applicant_pii, + :request_ip, + :should_proof_state_id, + :threatmetrix_session_id, + :timer, + :user_email + # @param [Hash] applicant_pii keys are symbols and values are strings, confidential user info # @param [Boolean] ipp_enrollment_in_progress flag that indicates if user will have # both state id address and current residential address verified @@ -27,40 +34,18 @@ def proof( user_email:, ipp_enrollment_in_progress: ) - device_profiling_result = proof_with_threatmetrix_if_needed( - applicant_pii: applicant_pii, - request_ip: request_ip, - threatmetrix_session_id: threatmetrix_session_id, - timer: timer, - user_email: user_email, - ) - - residential_instant_verify_result = proof_residential_address_if_needed( - applicant_pii: applicant_pii, - timer: timer, - ipp_enrollment_in_progress: ipp_enrollment_in_progress, - ) - - applicant_pii_transformed = applicant_pii.clone - if ipp_enrollment_in_progress - applicant_pii_transformed = with_state_id_address(applicant_pii_transformed) - end - - instant_verify_result = proof_id_address_with_lexis_nexis_if_needed( - applicant_pii: applicant_pii_transformed, - timer: timer, - residential_instant_verify_result: residential_instant_verify_result, - ipp_enrollment_in_progress: ipp_enrollment_in_progress, - ) - - state_id_result = proof_id_with_aamva_if_needed( - applicant_pii: applicant_pii_transformed, - timer: timer, - residential_instant_verify_result: residential_instant_verify_result, - instant_verify_result: instant_verify_result, - should_proof_state_id: should_proof_state_id, - ipp_enrollment_in_progress: ipp_enrollment_in_progress, - ) + @applicant_pii = applicant_pii + @request_ip = request_ip + @should_proof_state_id = should_proof_state_id + @threatmetrix_session_id = threatmetrix_session_id + @timer = timer + @user_email = user_email + @ipp_enrollment_in_progress = ipp_enrollment_in_progress + + @device_profiling_result = proof_with_threatmetrix_if_needed + @residential_instant_verify_result = proof_residential_address_if_needed + @instant_verify_result = proof_id_address_with_lexis_nexis_if_needed + @state_id_result = proof_id_with_aamva_if_needed ResultAdjudicator.new( device_profiling_result: device_profiling_result, @@ -75,13 +60,12 @@ def proof( private - def proof_with_threatmetrix_if_needed( - applicant_pii:, - user_email:, - threatmetrix_session_id:, - request_ip:, - timer: - ) + attr_reader :device_profiling_result, + :residential_instant_verify_result, + :instant_verify_result, + :state_id_result + + def proof_with_threatmetrix_if_needed unless FeatureManagement.proofing_device_profiling_collecting_enabled? return threatmetrix_disabled_result end @@ -101,15 +85,11 @@ def proof_with_threatmetrix_if_needed( end end - def proof_residential_address_if_needed( - applicant_pii:, - timer:, - ipp_enrollment_in_progress: false - ) - return residential_address_unnecessary_result unless ipp_enrollment_in_progress + def proof_residential_address_if_needed + return residential_address_unnecessary_result unless ipp_enrollment_in_progress? timer.time('residential address') do - resolution_proofer.proof(applicant_pii) + resolution_proofer.proof(applicant_pii_with_residential_address) end end @@ -125,68 +105,62 @@ def resolution_cannot_pass ) end - def proof_id_address_with_lexis_nexis_if_needed(applicant_pii:, timer:, - residential_instant_verify_result:, - ipp_enrollment_in_progress:) - if applicant_pii[:same_address_as_id] == 'true' && ipp_enrollment_in_progress + def proof_id_address_with_lexis_nexis_if_needed + if same_address_as_id? && ipp_enrollment_in_progress? return residential_instant_verify_result end return resolution_cannot_pass unless residential_instant_verify_result.success? timer.time('resolution') do - resolution_proofer.proof(applicant_pii) + resolution_proofer.proof(applicant_pii_with_state_id_address) end end - def should_proof_state_id_with_aamva?(ipp_enrollment_in_progress:, same_address_as_id:, - should_proof_state_id:, instant_verify_result:, - residential_instant_verify_result:) + def should_proof_state_id_with_aamva? return false unless should_proof_state_id # If the user is in in-person-proofing and they have changed their address then # they are not eligible for get-to-yes - if !ipp_enrollment_in_progress || same_address_as_id == 'true' - user_can_pass_after_state_id_check?(instant_verify_result) + if !ipp_enrollment_in_progress? || same_address_as_id? + user_can_pass_after_state_id_check? else residential_instant_verify_result.success? end end - def proof_id_with_aamva_if_needed( - applicant_pii:, timer:, - residential_instant_verify_result:, - instant_verify_result:, - should_proof_state_id:, - ipp_enrollment_in_progress: - ) - same_address_as_id = applicant_pii[:same_address_as_id] - should_proof_state_id_with_aamva = should_proof_state_id_with_aamva?( - ipp_enrollment_in_progress:, - same_address_as_id:, - should_proof_state_id:, - instant_verify_result:, - residential_instant_verify_result:, - ) - return out_of_aamva_jurisdiction_result unless should_proof_state_id_with_aamva + def proof_id_with_aamva_if_needed + return out_of_aamva_jurisdiction_result unless should_proof_state_id_with_aamva? timer.time('state_id') do - state_id_proofer.proof(applicant_pii) + state_id_proofer.proof(applicant_pii_with_state_id_address) end end - def user_can_pass_after_state_id_check?(resolution_result) - return true if resolution_result.success? + def user_can_pass_after_state_id_check? + return true if instant_verify_result.success? # For failed IV results, this method validates that the user is eligible to pass if the # failed attributes are covered by the same attributes in a successful AAMVA response # aka the Get-to-Yes w/ AAMVA feature. - return false unless resolution_result.failed_result_can_pass_with_additional_verification? + if !instant_verify_result.failed_result_can_pass_with_additional_verification? + return false + end attributes_aamva_can_pass = [:address, :dob, :state_id_number] + attributes_requiring_additional_verification = + instant_verify_result.attributes_requiring_additional_verification results_that_cannot_pass_aamva = - resolution_result.attributes_requiring_additional_verification - attributes_aamva_can_pass + attributes_requiring_additional_verification - attributes_aamva_can_pass results_that_cannot_pass_aamva.blank? end + def same_address_as_id? + applicant_pii[:same_address_as_id].to_s == 'true' + end + + def ipp_enrollment_in_progress? + @ipp_enrollment_in_progress + end + def threatmetrix_disabled_result Proofing::DdpResult.new( success: true, @@ -268,6 +242,18 @@ def state_id_proofer end end + def applicant_pii_with_state_id_address + if ipp_enrollment_in_progress? + with_state_id_address(applicant_pii) + else + applicant_pii + end + end + + def applicant_pii_with_residential_address + applicant_pii + end + # Make a copy of pii with the user's state ID address overwriting the address keys # Need to first remove the address keys to avoid key/value collision def with_state_id_address(pii) diff --git a/config/application.yml.default.prod b/config/application.yml.default.prod new file mode 100644 index 00000000000..f6af656607b --- /dev/null +++ b/config/application.yml.default.prod @@ -0,0 +1,8 @@ +# These secrets are used in CI to run a production build of assets, they are not used in live production requests +production: + secret_key_base: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + attribute_encryption_key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + hmac_fingerprinter_key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + password_pepper: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + piv_cac_verify_token_secret: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + session_encryption_key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index c54822457f4..4b03df082e6 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -85,6 +85,12 @@ search: ## Do not consider these keys missing: ignore_missing: + - datetime.dotiw.days + - datetime.dotiw.hours + - datetime.dotiw.less_than_x + - datetime.dotiw.months + - datetime.dotiw.weeks + - datetime.dotiw.years # - 'errors.messages.{accepted,blank,invalid,too_short,too_long}' # - '{devise,simple_form}.*' @@ -104,6 +110,17 @@ ignore_unused: - 'time.*' - 'datetime.dotiw.words_connector' - 'datetime.dotiw.last_word_connector' + - 'datetime.dotiw.days.one' + - 'datetime.dotiw.days.other' + - 'datetime.dotiw.hours.one' + - 'datetime.dotiw.hours.other' + - 'datetime.dotiw.less_than_x' + - 'datetime.dotiw.months.one' + - 'datetime.dotiw.months.other' + - 'datetime.dotiw.weeks.one' + - 'datetime.dotiw.weeks.other' + - 'datetime.dotiw.years.one' + - 'datetime.dotiw.years.other' ## Exclude these keys from the `i18n-tasks eq-base' report: # ignore_eq_base: # all: diff --git a/config/locales/es.yml b/config/locales/es.yml index 487df428305..e43c2a3117c 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -433,13 +433,24 @@ date.month_names: - noviembre - diciembre date.range: De %{from} a %{to} +datetime.dotiw.days.one: 1 día +datetime.dotiw.days.other: '%{count} días' +datetime.dotiw.hours.one: 1 hora +datetime.dotiw.hours.other: '%{count} horas' datetime.dotiw.last_word_connector: ' y ' +datetime.dotiw.less_than_x: 'menos de %{distance}' datetime.dotiw.minutes.one: un minuto datetime.dotiw.minutes.other: '%{count} minutos' +datetime.dotiw.months.one: 1 mes +datetime.dotiw.months.other: '%{count} meses' datetime.dotiw.seconds.one: un segundo datetime.dotiw.seconds.other: '%{count} segundos' datetime.dotiw.two_words_connector: ' y ' +datetime.dotiw.weeks.one: 1 semana +datetime.dotiw.weeks.other: '%{count} semanas' datetime.dotiw.words_connector: ', ' +datetime.dotiw.years.one: 1 año +datetime.dotiw.years.other: '%{count} años' devise.confirmations.already_confirmed: Su dirección de correo electrónico fue confirmada. %{action} devise.confirmations.confirmed: Usted confirmó su dirección de correo electrónico devise.confirmations.confirmed_but_must_set_password: Usted confirmó su dirección de correo electrónico @@ -1391,7 +1402,7 @@ notices.use_diff_email.text_html: O %{link_html} notices.webauthn_configured: Se agregó una clave de seguridad a su cuenta. notices.webauthn_platform_configured: Usó el bloqueo de pantalla de su dispositivo para agregar el desbloqueo facial o táctil a su cuenta. openid_connect.authorization.errors.bad_client_id: client_id incorrecto -openid_connect.authorization.errors.invalid_verified_within_duration.one: el valor debe ser al menos %{count} día o más +openid_connect.authorization.errors.invalid_verified_within_duration.one: el valor debe ser al menos %{count} días o más openid_connect.authorization.errors.invalid_verified_within_duration.other: el valor debe ser al menos %{count} días o más openid_connect.authorization.errors.invalid_verified_within_format: Formato no reconocido de verified_within openid_connect.authorization.errors.missing_ial: Falta un nivel válido de IAL @@ -1399,7 +1410,7 @@ openid_connect.authorization.errors.no_auth: Los acr_values no están autorizado openid_connect.authorization.errors.no_valid_acr_values: No se encontraron ial_values aceptables openid_connect.authorization.errors.no_valid_scope: No se encontraron valores de ámbito válidos openid_connect.authorization.errors.no_valid_vtr: vots encontrados no aceptables -openid_connect.authorization.errors.prompt_invalid: Prompt no es válido +openid_connect.authorization.errors.prompt_invalid: No se encontraron valores de consulta válidos openid_connect.authorization.errors.redirect_uri_invalid: redirect_uri no es válido openid_connect.authorization.errors.redirect_uri_no_match: redirect_uri no coincide con el redirect_uri registrado openid_connect.authorization.errors.unauthorized_scope: Ámbito no autorizado @@ -1410,10 +1421,11 @@ openid_connect.logout.errors.client_id_missing: Falta client_id openid_connect.logout.errors.id_token_hint: No se reconoció id_token_hint openid_connect.logout.errors.id_token_hint_present: Esta aplicación está mal configurada y no debería estar enviando id_token_hint. En vez de ello, enviar client_id. openid_connect.logout.errors.no_client_id_or_id_token_hint: Esta aplicación está mal configurada y debe enviar client_id o id_token_hint. -openid_connect.logout.heading: '¿Desea cerrar sesión en %{app_name}?' -openid_connect.logout.heading_with_sp: '¿Desea cerrar sesión en %{app_name} y volver a %{service_provider_name}?' +openid_connect.logout.heading: ¿Desea cerrar sesión en %{app_name}? +openid_connect.logout.heading_with_sp: ¿Desea cerrar sesión en %{app_name} y volver a %{service_provider_name}? + openid_connect.token.errors.expired_code: venció -openid_connect.token.errors.invalid_aud: Reclamación de audiencia no válida, %{url} previsto +openid_connect.token.errors.invalid_aud: Notificación de audiencia no válida, %{url} prevista openid_connect.token.errors.invalid_authentication: El cliente debe autenticarse a través de PKCE o private_key_jwt, omitiendo code_challenge o client_assertion openid_connect.token.errors.invalid_code: no es válido porque no coincide con ningún usuario. Consulte nuestra documentación en https://developers.login.gov/oidc/#token openid_connect.token.errors.invalid_code_verifier: code_verifier no coincide con code_challenge @@ -1427,19 +1439,19 @@ pages.page_took_too_long.header: El servidor tardó demasiado en procesar su sol report_mailer.deleted_accounts_report.issuers: Emisores report_mailer.deleted_accounts_report.name: Nombre report_mailer.deleted_accounts_report.subject: Informe de cuentas eliminadas -risc.security_event.errors.alg_unsupported: algoritmo no compatible, debe estar firmado con %{expected_alg} -risc.security_event.errors.aud_invalid: reclamo de auditor inválido, esperado %{url} -risc.security_event.errors.event_type_missing: event perdido +risc.security_event.errors.alg_unsupported: algoritmo no admitido, debe firmarse con %{expected_alg} +risc.security_event.errors.aud_invalid: notificación de audiencia no válida, prevista %{url} +risc.security_event.errors.event_type_missing: evento ausente risc.security_event.errors.event_type_unsupported: tipo de evento no admitido %{event_type} -risc.security_event.errors.exp_present: Los eventos SET no deben tener un reclamo de exp -risc.security_event.errors.jti_not_unique: jti ya existe -risc.security_event.errors.jti_required: se requiere reclamo jti +risc.security_event.errors.exp_present: los eventos SET no deben tener una notificación de vencimiento +risc.security_event.errors.jti_not_unique: jti no era único +risc.security_event.errors.jti_required: se necesita notificación jti risc.security_event.errors.jwt_could_not_parse: no se pudo analizar JWT -risc.security_event.errors.no_public_key: no se pudo cargar la clave pública para el emisor -risc.security_event.errors.sub_not_found: reclamo de event.subject.sub no válido -risc.security_event.errors.sub_unsupported: no se acepta subclase de nivel superior +risc.security_event.errors.no_public_key: no se pudo cargar la clave pública del emisor +risc.security_event.errors.sub_not_found: evento inválido.asunto.subnotificación +risc.security_event.errors.sub_unsupported: no se aceptó subnotificación de nivel superior risc.security_event.errors.subject_type_unsupported: subject_type debe ser %{expected_subject_type} -risc.security_event.errors.typ_error: El encabezado típico debe ser %{expected_typ} +risc.security_event.errors.typ_error: encabezado de tipo debe ser %{expected_typ} saml_idp.auth.error.title: Error saml_idp.shared.saml_post_binding.heading: Enviar para continuar saml_idp.shared.saml_post_binding.no_js: JavaScript parece estar desactivado en su navegador. Por lo general, este paso es automático, pero, debido a que tiene JavaScript desactivado, haga clic en el botón Enviar para continuar iniciando sesión o cerrándola. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 5d0aebd7d42..99ed1aa60a8 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -433,13 +433,24 @@ date.month_names: - 11月 - 12月 date.range: 从 %{from} 到 %{to} +datetime.dotiw.days.one: 1 天 +datetime.dotiw.days.other: '%{count} 天' +datetime.dotiw.hours.one: 1 小时 +datetime.dotiw.hours.other: '%{count} 小时' datetime.dotiw.last_word_connector: ' 和 ' +datetime.dotiw.less_than_x: 小于 %{distance} datetime.dotiw.minutes.one: 1 分钟 datetime.dotiw.minutes.other: '%{count} 分钟' +datetime.dotiw.months.one: 1 个月 +datetime.dotiw.months.other: '%{count} 个月' datetime.dotiw.seconds.one: 1 秒种 datetime.dotiw.seconds.other: '%{count} 秒' datetime.dotiw.two_words_connector: ' 和 ' +datetime.dotiw.weeks.one: 1 周 +datetime.dotiw.weeks.other: '%{count} 周' datetime.dotiw.words_connector: ', ' +datetime.dotiw.years.one: 1 年 +datetime.dotiw.years.other: '%{count} 年' devise.confirmations.already_confirmed: 你的电邮地址已确认。%{action} devise.confirmations.confirmed: 你已确认了你的电邮地址。 devise.confirmations.confirmed_but_must_set_password: 你已确认了你的电邮地址。 @@ -1396,56 +1407,56 @@ notices.use_diff_email.link: 使用一个不同的电邮地址 notices.use_diff_email.text_html: 或者,%{link_html} notices.webauthn_configured: 你账户添加了一个安全密钥。 notices.webauthn_platform_configured: 你用了自己设备的屏幕锁定给账户添加了人脸或触摸解锁。 -openid_connect.authorization.errors.bad_client_id: Bad client_id -openid_connect.authorization.errors.invalid_verified_within_duration.one: value must be at least %{count} day or older -openid_connect.authorization.errors.invalid_verified_within_duration.other: value must be at least %{count} days or older -openid_connect.authorization.errors.invalid_verified_within_format: Unrecognized format for verified_within -openid_connect.authorization.errors.missing_ial: Missing a valid IAL level -openid_connect.authorization.errors.no_auth: The acr_values are not authorized -openid_connect.authorization.errors.no_valid_acr_values: No acceptable acr_values found -openid_connect.authorization.errors.no_valid_scope: No valid scope values found +openid_connect.authorization.errors.bad_client_id: 坏的client_id +openid_connect.authorization.errors.invalid_verified_within_duration.one: 值必须至少为 %{count} 天或以上 +openid_connect.authorization.errors.invalid_verified_within_duration.other: 值必须至少为 %{count} 天或以上 +openid_connect.authorization.errors.invalid_verified_within_format: verified_within 格式无法识别 +openid_connect.authorization.errors.missing_ial: 缺失有效的 IAL 级别 +openid_connect.authorization.errors.no_auth: Acr_values 未经授权 +openid_connect.authorization.errors.no_valid_acr_values: 未找到可接受的 acr_values +openid_connect.authorization.errors.no_valid_scope: 未找到有效的范围值 openid_connect.authorization.errors.no_valid_vtr: No acceptable vots found -openid_connect.authorization.errors.prompt_invalid: No valid prompt values found -openid_connect.authorization.errors.redirect_uri_invalid: redirect_uri is invalid -openid_connect.authorization.errors.redirect_uri_no_match: redirect_uri does not match registered redirect_uri -openid_connect.authorization.errors.unauthorized_scope: Unauthorized scope -openid_connect.logout.confirm: Yes, sign out of %{app_name} -openid_connect.logout.deny: No, go to my account page -openid_connect.logout.errors.client_id_invalid: client_id was not recognized -openid_connect.logout.errors.client_id_missing: client_id is missing -openid_connect.logout.errors.id_token_hint: id_token_hint was not recognized -openid_connect.logout.errors.id_token_hint_present: This application is misconfigured and should not be sending id_token_hint. Please send client_id instead. -openid_connect.logout.errors.no_client_id_or_id_token_hint: This application is misconfigured and must send either client_id or id_token_hint. -openid_connect.logout.heading: Do you want to sign out of %{app_name}? -openid_connect.logout.heading_with_sp: Do you want to sign out of %{app_name} and return to %{service_provider_name}? -openid_connect.token.errors.expired_code: is expired -openid_connect.token.errors.invalid_aud: Invalid audience claim, expected %{url} -openid_connect.token.errors.invalid_authentication: Client must authenticate via PKCE or private_key_jwt, missing either code_challenge or client_assertion -openid_connect.token.errors.invalid_code: is invalid because doesn’t match any user. Please see our documentation at https://developers.login.gov/oidc/#token -openid_connect.token.errors.invalid_code_verifier: code_verifier did not match code_challenge -openid_connect.token.errors.invalid_iat: iat must be an integer or floating point Unix timestamp representing a time in the past -openid_connect.token.errors.invalid_signature: Could not validate assertion against any registered public keys -openid_connect.user_info.errors.malformed_authorization: Malformed Authorization header -openid_connect.user_info.errors.no_authorization: No Authorization header provided -openid_connect.user_info.errors.not_found: Could not find authorization for the contents of the provided access_token or it may have expired +openid_connect.authorization.errors.prompt_invalid: 未找到有效的提示值 +openid_connect.authorization.errors.redirect_uri_invalid: redirect_uri 有误 +openid_connect.authorization.errors.redirect_uri_no_match: redirect_uri 与注册的 redirect_uri 不匹配 +openid_connect.authorization.errors.unauthorized_scope: 未经授权的范围 +openid_connect.logout.confirm: 是的,登出 %{app_name} +openid_connect.logout.deny: 不,到我的账户页面 +openid_connect.logout.errors.client_id_invalid: client_id 不认识 +openid_connect.logout.errors.client_id_missing: client_id 缺失 +openid_connect.logout.errors.id_token_hint: id_token_hint 不认识 +openid_connect.logout.errors.id_token_hint_present: 本应用程序配置错误,不应发送 id_token_hint。请发送 client_id。 +openid_connect.logout.errors.no_client_id_or_id_token_hint: 本应用程序配置错误,必须发送 client_id 或者 id_token_hint。 +openid_connect.logout.heading: 你想登出 %{app_name} 吗? +openid_connect.logout.heading_with_sp: 你想登出 %{app_name} 并回到 %{service_provider_name} 吗? +openid_connect.token.errors.expired_code: 过期了 +openid_connect.token.errors.invalid_aud: 受众声明有误,预期 %{url} +openid_connect.token.errors.invalid_authentication: 客户必须通过 PKCE 或 private_key_jwt 来验证,缺失 code_challenge 或者 client_assertion +openid_connect.token.errors.invalid_code: 无效,因为与任何用户都匹配不上。请参阅我们的文档: https://developers.login.gov/oidc/#token +openid_connect.token.errors.invalid_code_verifier: code_verifier 与 code_challenge 不匹配 +openid_connect.token.errors.invalid_iat: iat 必须是整数或者浮动点 Unix 时间印章来代表过去的一个时间 +openid_connect.token.errors.invalid_signature: 无法根据任何已注册的公钥验证这个说法 +openid_connect.user_info.errors.malformed_authorization: 授权标头畸形 +openid_connect.user_info.errors.no_authorization: 未提供授权标头 +openid_connect.user_info.errors.not_found: 就提供的 access_token 内容找不到授权或者授权已过期。 pages.page_took_too_long.body: 你也许想等几分钟后再试。(503) pages.page_took_too_long.header: 服务器处理你请求的时间过长。 report_mailer.deleted_accounts_report.issuers: 发放方 report_mailer.deleted_accounts_report.name: 姓名 report_mailer.deleted_accounts_report.subject: 已删除账户报告 -risc.security_event.errors.alg_unsupported: unsupported algorithm, must be signed with %{expected_alg} -risc.security_event.errors.aud_invalid: invalid aud claim, expected %{url} -risc.security_event.errors.event_type_missing: missing event -risc.security_event.errors.event_type_unsupported: unsupported event type %{event_type} -risc.security_event.errors.exp_present: SET events must not have an exp claim -risc.security_event.errors.jti_not_unique: jti was not unique -risc.security_event.errors.jti_required: jti claim is required -risc.security_event.errors.jwt_could_not_parse: could not parse JWT -risc.security_event.errors.no_public_key: could not load public key for issuer -risc.security_event.errors.sub_not_found: invalid event.subject.sub claim -risc.security_event.errors.sub_unsupported: top-level sub claim is not accepted -risc.security_event.errors.subject_type_unsupported: subject_type must be %{expected_subject_type} -risc.security_event.errors.typ_error: typ header must be %{expected_typ} +risc.security_event.errors.alg_unsupported: 不支持的算法,必须用 %{expected_alg} 签名 +risc.security_event.errors.aud_invalid: 受众声明无效,预期 %{url} +risc.security_event.errors.event_type_missing: 缺失事件 +risc.security_event.errors.event_type_unsupported: 不支持的事件类型 %{event_type} +risc.security_event.errors.exp_present: SET 事件不能有过期声明 +risc.security_event.errors.jti_not_unique: jti 没有独特性 +risc.security_event.errors.jti_required: 要求有 jti 声索 +risc.security_event.errors.jwt_could_not_parse: 无法解析 JWT +risc.security_event.errors.no_public_key: 无法为发放方加载公钥 +risc.security_event.errors.sub_not_found: event.subject.sub 声明无效 +risc.security_event.errors.sub_unsupported: 顶层主题声索不被接受 +risc.security_event.errors.subject_type_unsupported: subject_type 必须是 %{expected_subject_type} +risc.security_event.errors.typ_error: 类型标头必须是 %{expected_typ} saml_idp.auth.error.title: 错误 saml_idp.shared.saml_post_binding.heading: 提交来继续 saml_idp.shared.saml_post_binding.no_js: 你浏览器中的 JavaScript 似乎已关闭。该步骤通常会自动发生,但因为你把 JavaScript 关闭,请点击提交按钮来继续登入或登出。 diff --git a/dockerfiles/idp_prod.Dockerfile b/dockerfiles/idp_prod.Dockerfile new file mode 100644 index 00000000000..bd462848476 --- /dev/null +++ b/dockerfiles/idp_prod.Dockerfile @@ -0,0 +1,177 @@ +FROM ruby:3.3.1-slim + +# Set environment variables +ENV RAILS_ROOT /app +ENV RAILS_ENV production +ENV NODE_ENV production +ENV RAILS_SERVE_STATIC_FILES true +ENV RAILS_LOG_TO_STDOUT true +ENV RAILS_LOG_LEVEL debug +ENV BUNDLE_PATH /usr/local/bundle +ENV YARN_VERSION 1.22.5 +ENV NODE_VERSION 20.10.0 +ENV BUNDLER_VERSION 2.5.6 +ENV POSTGRES_SSLMODE prefer +ENV POSTGRES_NAME idp +ENV POSTGRES_HOST postgres +ENV POSTGRES_USERNAME postgres +ENV POSTGRES_PASSWORD postgres +ENV POSTGRES_WORKER_SSLMODE prefer +ENV POSTGRES_WORKER_NAME idp-worker-jobs +ENV POSTGRES_WORKER_HOST postgres-worker +ENV POSTGRES_WORKER_USERNAME postgres +ENV POSTGRES_WORKER_PASSWORD postgres +ENV REDIS_IRS_ATTEMPTS_API_URL redis://redis:6379/2 +ENV REDIS_THROTTLE_URL redis://redis:6379/1 +ENV REDIS_URL redis://redis:6379 +ENV ASSET_HOST http://localhost:3000 +ENV DOMAIN_NAME localhost:3000 +ENV PIV_CAC_SERVICE_URL https://localhost:8443/ +ENV PIV_CAC_VERIFY_TOKEN_URL https://localhost:8443/ + +# Install dependencies +RUN apt-get update && \ + apt-get install -y \ + git-core \ + git-lfs \ + curl \ + zlib1g-dev \ + build-essential \ + libssl-dev \ + libreadline-dev \ + libyaml-dev \ + libsqlite3-dev \ + sqlite3 \ + libxml2-dev \ + libxslt1-dev \ + libcurl4-openssl-dev \ + software-properties-common \ + libffi-dev \ + libpq-dev \ + unzip && \ + rm -rf /var/lib/apt/lists/* + +RUN curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \ + && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ + && rm "node-v$NODE_VERSION-linux-x64.tar.xz" \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejsv + +# Install Yarn +RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn-archive-keyring.gpg >/dev/null +RUN echo "deb [signed-by=/usr/share/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list +RUN apt-get update && apt-get install -y yarn=1.22.5-1 + +# Download RDS Combined CA Bundle +RUN mkdir -p /usr/local/share/aws \ + && curl https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem > /usr/local/share/aws/rds-combined-ca-bundle.pem \ + && chmod 644 /usr/local/share/aws/rds-combined-ca-bundle.pem + +# Create a new user and set up the working directory +RUN addgroup --gid 1000 app && \ + adduser --uid 1000 --gid 1000 --disabled-password --gecos "" app && \ + mkdir -p $RAILS_ROOT && \ + mkdir -p $BUNDLE_PATH && \ + mkdir -p $RAILS_ROOT/tmp/pids && \ + mkdir -p $RAILS_ROOT/log + +# Setup timezone data +ENV TZ=Etc/UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# Create the working directory +WORKDIR $RAILS_ROOT + +COPY .ruby-version $RAILS_ROOT/.ruby-version +COPY Gemfile $RAILS_ROOT/Gemfile +COPY Gemfile.lock $RAILS_ROOT/Gemfile.lock + +RUN bundle config build.nokogiri --use-system-libraries +RUN bundle config set --local deployment 'true' +RUN bundle config set --local path $BUNDLE_PATH +RUN bundle config set --local without 'deploy development doc test' +RUN bundle install --jobs $(nproc) +RUN bundle binstubs --all + +COPY package.json $RAILS_ROOT/package.json +COPY yarn.lock $RAILS_ROOT/yarn.lock +RUN yarn install --production=true --frozen-lockfile --cache-folder .yarn-cache + +# Add the application code +COPY ./lib ./lib +COPY ./app ./app +COPY ./config ./config +COPY ./config.ru ./config.ru +COPY ./db ./db +COPY ./deploy ./deploy +COPY ./bin ./bin +COPY ./public ./public +COPY ./scripts ./scripts +COPY ./spec ./spec +COPY ./Rakefile ./Rakefile +COPY ./Makefile ./Makefile +COPY ./babel.config.js ./babel.config.js +COPY ./webpack.config.js ./webpack.config.js +COPY ./.browserslistrc ./.browserslistrc + +# Copy keys +COPY keys.example $RAILS_ROOT/keys + +# Copy big files +ARG LARGE_FILES_USER +ARG LARGE_FILES_TOKEN +RUN mkdir -p $RAILS_ROOT/geo_data && chmod 755 $RAILS_ROOT/geo_data +RUN mkdir -p $RAILS_ROOT/pwned_passwords && chmod 755 $RAILS_ROOT/pwned_passwords +RUN git clone --depth 1 https://$LARGE_FILES_USER:$LARGE_FILES_TOKEN@gitlab.login.gov/lg-public/idp-large-files.git && \ + cp idp-large-files/GeoIP2-City.mmdb $RAILS_ROOT/geo_data/ && \ + cp idp-large-files/GeoLite2-City.mmdb $RAILS_ROOT/geo_data/ && \ + cp idp-large-files/pwned-passwords.txt $RAILS_ROOT/pwned_passwords/ && \ + rm -r idp-large-files +RUN mkdir -p /usr/local/share/aws && \ + curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem > /usr/local/share/aws/rds-combined-ca-bundle.pem + +# Copy robots.txt +COPY public/ban-robots.txt $RAILS_ROOT/public/robots.txt + +# Copy application.yml.default to application.yml +COPY ./config/application.yml.default.prod $RAILS_ROOT/config/application.yml + +# Setup config files +COPY config/agencies.localdev.yml $RAILS_ROOT/config/agencies.yml +COPY config/iaa_gtcs.localdev.yml $RAILS_ROOT/config/iaa_gtcs.yml +COPY config/iaa_orders.localdev.yml $RAILS_ROOT/config/iaa_orders.yml +COPY config/iaa_statuses.localdev.yml $RAILS_ROOT/config/iaa_statuses.yml +COPY config/integration_statuses.localdev.yml $RAILS_ROOT/config/integration_statuses.yml +COPY config/integrations.localdev.yml $RAILS_ROOT/config/integrations.yml +COPY config/partner_account_statuses.localdev.yml $RAILS_ROOT/config/partner_account_statuses.yml +COPY config/partner_accounts.localdev.yml $RAILS_ROOT/config/partner_accounts.yml +COPY certs.example $RAILS_ROOT/certs +COPY config/service_providers.localdev.yml $RAILS_ROOT/config/service_providers.yml + +# Precompile assets +RUN bundle exec rake assets:precompile --trace + +ARG ARG_CI_COMMIT_BRANCH="branch_placeholder" +ARG ARG_CI_COMMIT_SHA="sha_placeholder" +RUN mkdir -p $RAILS_ROOT/public/api/ +RUN echo "{\"branch\":\"$ARG_CI_COMMIT_BRANCH\",\"git_sha\":\"$ARG_CI_COMMIT_SHA\"}" > $RAILS_ROOT/public/api/deploy.json + +# Generate and place SSL certificates for puma +RUN openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 1825 \ + -keyout $RAILS_ROOT/keys/localhost.key \ + -out $RAILS_ROOT/keys/localhost.crt \ + -subj "/C=US/ST=Fake/L=Fakerton/O=Dis/CN=localhost" + +# make everything the proper perms after everything is initialized +RUN chown -R app:app $RAILS_ROOT/tmp && \ + chown -R app:app $RAILS_ROOT/log && \ + find $RAILS_ROOT -type d | xargs chmod 755 + +# Expose the port the app runs on +EXPOSE 3000 + +# Set user +USER app + +# Start the application +CMD ["bundle", "exec", "puma", "-b", "ssl://0.0.0.0:3000?key=/app/keys/localhost.key&cert=/app/keys/localhost.crt"] + diff --git a/lib/saml_idp_constants.rb b/lib/saml_idp_constants.rb index b50d4e02454..46b37ae8bb4 100644 --- a/lib/saml_idp_constants.rb +++ b/lib/saml_idp_constants.rb @@ -30,6 +30,7 @@ module Constants NAME_ID_FORMAT_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' NAME_ID_FORMAT_EMAIL = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' + NAME_ID_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' VALID_NAME_ID_FORMATS = [NAME_ID_FORMAT_PERSISTENT, NAME_ID_FORMAT_EMAIL].freeze REQUESTED_ATTRIBUTES_CLASSREF = 'http://idmanagement.gov/ns/requested_attributes?ReqAttr=' diff --git a/scripts/enforce-typescript-files.mjs b/scripts/enforce-typescript-files.mjs index 14e3d10abea..02ef7efe969 100755 --- a/scripts/enforce-typescript-files.mjs +++ b/scripts/enforce-typescript-files.mjs @@ -61,7 +61,6 @@ const LEGACY_FILE_EXCEPTIONS = [ 'spec/javascript/packages/document-capture/context/acuant-spec.jsx', 'spec/javascript/packages/document-capture/context/device-spec.jsx', 'spec/javascript/packages/document-capture/context/failed-capture-attempts-spec.jsx', - 'spec/javascript/packages/document-capture/context/feature-flag-spec.jsx', 'spec/javascript/packages/document-capture/context/file-base64-cache-spec.js', 'spec/javascript/packages/document-capture/context/index-spec.js', 'spec/javascript/packages/document-capture/context/selfie-capture-spec.jsx', @@ -83,15 +82,24 @@ const packagesWithEntrypoints = await glob('app/javascript/packages/*/package.js const jsFiles = await glob( ['app/{javascript/packages,components}/**/*.{js,jsx}', 'spec/javascript/*/**/*.{js,jsx}'], - { - ignore: [...packagesWithEntrypoints.map((path) => join(path, '**')), ...LEGACY_FILE_EXCEPTIONS], - }, + { ignore: packagesWithEntrypoints.map((path) => join(path, '**')) }, ); +const invalidExceptions = LEGACY_FILE_EXCEPTIONS.filter((file) => !jsFiles.includes(file)); + +assert( + !invalidExceptions.length, + `Unnecessary exception should be removed from LEGACY_FILE_EXCEPTIONS allowlist. + +Found ${JSON.stringify(invalidExceptions)}`, +); + +const unexpectedJSFiles = jsFiles.filter((file) => !LEGACY_FILE_EXCEPTIONS.includes(file)); + assert( - !jsFiles.length, + !unexpectedJSFiles.length, `All new JavaScript files should be written with TypeScript extensions (.ts, .tsx). -Found ${JSON.stringify(jsFiles)} +Found ${JSON.stringify(unexpectedJSFiles)} `, ); diff --git a/spec/controllers/account_reset/delete_account_controller_spec.rb b/spec/controllers/account_reset/delete_account_controller_spec.rb index 4f147b4ddf9..8be4a6ba3a5 100644 --- a/spec/controllers/account_reset/delete_account_controller_spec.rb +++ b/spec/controllers/account_reset/delete_account_controller_spec.rb @@ -22,6 +22,7 @@ user_id: user.uuid, success: true, errors: {}, + error_details: nil, mfa_method_counts: { backup_codes: 10, webauthn: 2, phone: 2 }, pii_like_keypaths: [[:mfa_method_counts, :phone]], account_age_in_days: 0, diff --git a/spec/controllers/concerns/two_factor_authenticatable_methods_spec.rb b/spec/controllers/concerns/two_factor_authenticatable_methods_spec.rb index 7ba560c4fc2..035103d2ce0 100644 --- a/spec/controllers/concerns/two_factor_authenticatable_methods_spec.rb +++ b/spec/controllers/concerns/two_factor_authenticatable_methods_spec.rb @@ -88,9 +88,48 @@ expect(args[:disavowal_event]).to be_kind_of(Event) expect(args[:disavowal_token]).to be_kind_of(String) end - result end + + context 'sign_in_notification_timeframe_expired missing' do + it 'tracks analytics event for missing timeframe_expired' do + stub_analytics + result + + expect(@analytics).to have_logged_event( + :sign_in_notification_timeframe_expired_absent, + ) + end + end + + context 'sign_in_notification_timeframe_expired present' do + before do + create( + :event, + user:, + event_type: :sign_in_notification_timeframe_expired, + created_at: 10.minutes.ago, + ) + end + + around do |ex| + freeze_time { ex.run } + end + + it 'creates a new user event with disavowal' do + expect(UserAlerts::AlertUserAboutNewDevice).to receive(:send_alert) do |**args| + expect(user.reload.sign_in_new_device_at.change(usec: 0)).to eq( + 10.minutes.ago, + ) + end + stub_analytics + result + + expect(@analytics).to_not have_logged_event( + :sign_in_notification_timeframe_expired_absent, + ) + end + end end end end diff --git a/spec/controllers/idv/by_mail/enter_code_controller_spec.rb b/spec/controllers/idv/by_mail/enter_code_controller_spec.rb index afc63cc6d7e..d49a7995d64 100644 --- a/spec/controllers/idv/by_mail/enter_code_controller_spec.rb +++ b/spec/controllers/idv/by_mail/enter_code_controller_spec.rb @@ -201,6 +201,7 @@ 'IdV: enter verify by mail code submitted', success: true, errors: {}, + error_details: nil, pending_in_person_enrollment: false, fraud_check_failed: false, enqueued_at: pending_profile.gpo_confirmation_codes.last.code_sent_at, @@ -243,6 +244,7 @@ 'IdV: enter verify by mail code submitted', success: true, errors: {}, + error_details: nil, pending_in_person_enrollment: true, fraud_check_failed: false, enqueued_at: pending_profile.gpo_confirmation_codes.last.code_sent_at, @@ -271,6 +273,7 @@ 'IdV: enter verify by mail code submitted', success: true, errors: {}, + error_details: nil, pending_in_person_enrollment: false, fraud_check_failed: true, enqueued_at: pending_profile.gpo_confirmation_codes.last.code_sent_at, @@ -299,6 +302,7 @@ 'IdV: enter verify by mail code submitted', success: true, errors: {}, + error_details: nil, pending_in_person_enrollment: false, fraud_check_failed: true, enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at, @@ -332,6 +336,7 @@ 'IdV: enter verify by mail code submitted', success: true, errors: {}, + error_details: nil, pending_in_person_enrollment: false, fraud_check_failed: true, enqueued_at: user.pending_profile.gpo_confirmation_codes.last.code_sent_at, diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb index e83986cf62f..f029a085d88 100644 --- a/spec/controllers/idv/image_uploads_controller_spec.rb +++ b/spec/controllers/idv/image_uploads_controller_spec.rb @@ -344,6 +344,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -406,6 +407,7 @@ 'IdV: doc auth image upload vendor pii validation', success: true, errors: {}, + error_details: nil, attention_with_barcode: false, user_id: user.uuid, submit_attempts: 1, @@ -488,6 +490,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -585,6 +588,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -682,6 +686,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -776,6 +781,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -894,6 +900,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, @@ -984,6 +991,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: IdentityConfig.store.doc_auth_max_attempts - 1, diff --git a/spec/controllers/idv/in_person/state_id_controller_spec.rb b/spec/controllers/idv/in_person/state_id_controller_spec.rb index eb6ec940606..881484d7bb8 100644 --- a/spec/controllers/idv/in_person/state_id_controller_spec.rb +++ b/spec/controllers/idv/in_person/state_id_controller_spec.rb @@ -166,6 +166,7 @@ { success: true, errors: {}, + error_details: nil, analytics_id: 'In Person Proofing', flow_path: 'standard', step: 'state_id', diff --git a/spec/controllers/idv/otp_verification_controller_spec.rb b/spec/controllers/idv/otp_verification_controller_spec.rb index 775ce466c22..cf8fb56a3f5 100644 --- a/spec/controllers/idv/otp_verification_controller_spec.rb +++ b/spec/controllers/idv/otp_verification_controller_spec.rb @@ -163,6 +163,7 @@ expected_result = { success: true, errors: {}, + error_details: nil, code_expired: false, code_matches: true, otp_delivery_preference: :sms, diff --git a/spec/controllers/idv/phone_controller_spec.rb b/spec/controllers/idv/phone_controller_spec.rb index 79cd0969e2c..c09fff3170b 100644 --- a/spec/controllers/idv/phone_controller_spec.rb +++ b/spec/controllers/idv/phone_controller_spec.rb @@ -348,6 +348,7 @@ result = { success: true, errors: {}, + error_details: nil, area_code: '703', country_code: 'US', carrier: 'Test Mobile Carrier', @@ -444,6 +445,7 @@ new_phone_added: true, hybrid_handoff_phone_used: false, errors: {}, + error_details: nil, phone_fingerprint: Pii::Fingerprinter.fingerprint(proofing_phone.e164), country_code: proofing_phone.country, area_code: proofing_phone.area_code, diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index 8d71d73ce0f..80052b3ae49 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -422,4 +422,43 @@ end end end + + describe '#add_proofing_costs' do + let(:sp_costs_added) { nil } + let(:result) do + { + context: { + sp_costs_added:, + stages: { + resolution: { + transaction_id: 'ABCD1234', + }, + residential_address: { + vendor_name: 'ResidentialAddressNotRequired', + }, + state_id: { + transaction_id: 'EFGH5678', + }, + threatmetrix: { + transaction_id: 'IJKL9012', + }, + }, + }, + } + end + + it 'adds proofing costs' do + expect(subject).to receive(:add_cost).exactly(3).times + subject.send(:add_proofing_costs, result) + end + + context 'when proofing costs have already been added' do + let(:sp_costs_added) { true } + + it 'does not add proofing costs' do + expect(subject).not_to receive(:add_cost) + subject.send(:add_proofing_costs, result) + end + end + end end diff --git a/spec/controllers/openid_connect/authorization_controller_spec.rb b/spec/controllers/openid_connect/authorization_controller_spec.rb index dc3fb031eec..0cf937f8e6a 100644 --- a/spec/controllers/openid_connect/authorization_controller_spec.rb +++ b/spec/controllers/openid_connect/authorization_controller_spec.rb @@ -1,7 +1,7 @@ # rubocop:disable Layout/LineLength require 'rails_helper' -RSpec.describe OpenidConnect::AuthorizationController, allowed_extra_analytics: [:*] do +RSpec.describe OpenidConnect::AuthorizationController do include WebAuthnHelper before do # All the tests here were written prior to the interstitial @@ -133,6 +133,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', @@ -189,6 +190,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: true, acr_values: '', @@ -386,6 +388,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', @@ -762,6 +765,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -855,6 +859,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -951,6 +956,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -1118,6 +1124,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', @@ -1176,6 +1183,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: true, acr_values: '', @@ -1374,6 +1382,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', @@ -1752,6 +1761,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -1845,6 +1855,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -1941,6 +1952,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: false, user_fully_authenticated: true, acr_values: 'http://idmanagement.gov/ns/assurance/ial/0', @@ -2490,6 +2502,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: false, acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', @@ -2623,6 +2636,7 @@ referer: nil, allow_prompt_login: true, errors: {}, + error_details: nil, unauthorized_scope: true, user_fully_authenticated: false, acr_values: '', diff --git a/spec/controllers/openid_connect/user_info_controller_spec.rb b/spec/controllers/openid_connect/user_info_controller_spec.rb index 6f9244a9a76..40467f5ea85 100644 --- a/spec/controllers/openid_connect/user_info_controller_spec.rb +++ b/spec/controllers/openid_connect/user_info_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe OpenidConnect::UserInfoController, allowed_extra_analytics: [:*] do +RSpec.describe OpenidConnect::UserInfoController do let(:json_response) { JSON.parse(response.body).with_indifferent_access } describe '#show' do @@ -133,6 +133,7 @@ client_id: identity.service_provider, ial: identity.ial, errors: {}, + error_details: nil, ) action diff --git a/spec/controllers/risc/security_events_controller_spec.rb b/spec/controllers/risc/security_events_controller_spec.rb index 8799871775b..55cf7ef677f 100644 --- a/spec/controllers/risc/security_events_controller_spec.rb +++ b/spec/controllers/risc/security_events_controller_spec.rb @@ -53,6 +53,7 @@ event_type: event_type, error_code: nil, errors: {}, + error_details: nil, jti: jti, success: true, user_id: user.uuid) diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index ad69564b0b7..f7eafdfe021 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -121,7 +121,7 @@ # the RubySAML library won't let us pass an empty string in as the certificate # element, so this test substitutes a SAMLRequest that has that element blank let(:blank_cert_element_req) do - <<-XML.gsub(/^[\s\t]*|[\s\t]*\n/, '') + <<-XML.gsub(/^[\s]+|[\s]+\n/, '') http://localhost:3000 @@ -788,20 +788,24 @@ def name_id_version(format_urn) user_fully_authenticated: true, }) expect(@analytics).to receive(:track_event). - with('SAML Auth', { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: [Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF], - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - service_provider: sp1_issuer, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - }) + with( + 'SAML Auth', + hash_including( + success: true, + errors: {}, + error_details: nil, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: [Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF], + authn_context_comparison: 'exact', + requested_ial: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + service_provider: sp1_issuer, + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + request_signed: true, + matching_cert_serial: saml_test_sp_cert_serial, + ), + ) expect(@analytics).to receive(:track_event).with( 'SP redirect initiated', ial: Idp::Constants::IAL2, @@ -937,20 +941,24 @@ def name_id_version(format_urn) user_fully_authenticated: true, }) expect(@analytics).to receive(:track_event). - with('SAML Auth', { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: ['http://idmanagement.gov/ns/assurance/ial/1'], - authn_context_comparison: 'minimum', - requested_ial: 'ialmax', - service_provider: sp1_issuer, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - }) + with( + 'SAML Auth', + hash_including( + success: true, + errors: {}, + error_details: nil, + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + authn_context: ['http://idmanagement.gov/ns/assurance/ial/1'], + authn_context_comparison: 'minimum', + requested_ial: 'ialmax', + service_provider: sp1_issuer, + endpoint: "/api/saml/auth#{path_year}", + idv: false, + finish_profile: false, + request_signed: true, + matching_cert_serial: saml_test_sp_cert_serial, + ), + ) expect(@analytics).to receive(:track_event).with( 'SP redirect initiated', ial: 0, @@ -1007,7 +1015,7 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end end @@ -1232,7 +1240,7 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end end @@ -1281,7 +1289,7 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end end @@ -1439,52 +1447,6 @@ def name_id_version(format_urn) end end - context 'service provider uses email NameID format and is allowed to use email' do - let(:user) { create(:user, :fully_registered) } - - before do - settings = saml_settings( - overrides: { - issuer: sp1_issuer, - name_identifier_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL, - }, - ) - ServiceProvider. - find_by(issuer: settings.issuer). - update!(email_nameid_format_allowed: true) - generate_saml_response(user, settings) - end - - # Testing the element when the SP is configured to use a - # NameID format of emailAddress rather than the default persistent UUID. - context 'Subject' do - let(:subject) do - xmldoc.subject_nodeset[0] - end - - it 'has a saml:Subject element' do - expect(subject).to_not be_nil - end - - context 'NameID' do - let(:name_id) { subject.at('//ds:NameID', ds: Saml::XML::Namespaces::ASSERTION) } - - it 'has a saml:NameID element' do - expect(name_id).to_not be_nil - end - - it 'has a format attribute defining the NameID to be email' do - expect(name_id.attributes['Format'].value). - to eq(Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL) - end - - it 'has NameID value of the email address of the user making the AuthN Request' do - expect(name_id.children.first.to_s).to eq(user.email) - end - end - end - end - context 'no matching cert from the SAML request' do let(:user) { create(:user, :fully_registered) } @@ -1515,6 +1477,7 @@ def name_id_version(format_urn) analytics_hash = { success: true, errors: {}, + error_details: nil, nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, authn_context: [ Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, @@ -1531,7 +1494,7 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end end @@ -1547,7 +1510,7 @@ def name_id_version(format_urn) # the RubySAML library won't let us pass an empty string in as the certificate # element, so this test substitutes a SAMLRequest that has that element blank let(:blank_cert_element_req) do - <<-XML.gsub(/^[\s\t]*|[\s\t]*\n/, '') + <<-XML.gsub(/^[\s]+|[\s]+\n/, '') http://localhost:3000 @@ -1609,7 +1572,7 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end it 'returns a 400' do @@ -1644,6 +1607,7 @@ def name_id_version(format_urn) analytics_hash = { success: true, errors: {}, + error_details: nil, nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, authn_context: [Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF], authn_context_comparison: 'exact', @@ -1657,221 +1621,180 @@ def name_id_version(format_urn) } expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) end end - context 'nameid_format is missing' do + describe 'NameID format' do let(:user) { create(:user, :fully_registered) } + let(:subject_element) { xmldoc.subject_nodeset[0] } + let(:name_id) { subject_element.at('//ds:NameID', ds: Saml::XML::Namespaces::ASSERTION) } + let(:auth_settings) { saml_settings(overrides: { name_identifier_format: }) } + let(:name_identifier_format) { nil } + let(:email_allowed) { nil } + let(:use_legacy_name_id_behavior) { nil } before do stub_analytics - allow(@analytics).to receive(:track_event) + service_provider = ServiceProvider.find_by(issuer: auth_settings.issuer) + IdentityLinker.new(user, service_provider).link_identity + service_provider.update!( + use_legacy_name_id_behavior:, + email_nameid_format_allowed: email_allowed, + ) end - it 'defaults to persistent' do - auth_settings = saml_settings(overrides: { name_identifier_format: nil }) - service_provider = build(:service_provider, issuer: auth_settings.issuer) - IdentityLinker.new(user, service_provider).link_identity - user.identities.last.update!(verified_attributes: ['email']) - generate_saml_response(user, auth_settings) + shared_examples_for 'sends the UUID' do |requested_nameid_format| + it 'sends the UUID' do + generate_saml_response(user, auth_settings) - expect(response.status).to eq(200) + expect(response.status).to eq(200) + expect(name_id.attributes['Format'].value). + to eq(Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT) + expect(name_id.children.first.to_s).to eq(user.last_identity.uuid) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, + requested_nameid_format: requested_nameid_format, + success: true, + ), + ) + end + end - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } + shared_examples_for 'sends the email' do |requested_nameid_format| + it 'sends the email' do + generate_saml_response(user, auth_settings) - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + expect(response.status).to eq(200) + expect(name_id.attributes['Format'].value). + to eq(Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL) + expect(name_id.children.first.to_s).to eq(user.email) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL, + requested_nameid_format: requested_nameid_format, + success: true, + ), + ) + end end - it 'defaults to email when added to issuers_with_email_nameid_format' do - auth_settings = saml_settings( - overrides: { - issuer: sp1_issuer, - name_identifier_format: nil, - }, - ) - ServiceProvider. - find_by(issuer: auth_settings.issuer). - update!(email_nameid_format_allowed: true) - IdentityLinker.new(user, sp1).link_identity - user.identities.last.update!(verified_attributes: ['email']) - generate_saml_response(user, auth_settings) + shared_examples_for 'returns an unauthorized nameid error' do |requested_nameid_format| + it 'returns an error' do + generate_saml_response(user, auth_settings) - expect(response.status).to eq(200) + expect(controller).to render_template('saml_idp/auth/error') + expect(response.status).to eq(400) + expect(response.body).to include(t('errors.messages.unauthorized_nameid_format')) + expect(@analytics).to have_logged_event( + 'SAML Auth', + hash_including( + nameid_format: requested_nameid_format, + requested_nameid_format: requested_nameid_format, + success: false, + ), + ) + end + end - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: auth_settings.issuer, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } + context 'when the NameID format has the value "unspecified"' do + let(:name_identifier_format) { Saml::Idp::Constants::NAME_ID_FORMAT_UNSPECIFIED } - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) - end - end + context 'when the service provider is not configured with use_legacy_name_id_behavior' do + let(:use_legacy_name_id_behavior) { false } - context 'service provider uses email NameID format but is not allowed to use email' do - it 'returns an error' do - stub_analytics - allow(@analytics).to receive(:track_event) + it_behaves_like 'sends the UUID', Saml::Idp::Constants::NAME_ID_FORMAT_UNSPECIFIED + end + context 'when the service provider is configured with use_legacy_name_id_behavior' do + let(:use_legacy_name_id_behavior) { true } + it 'sends the id, not the UUID' do + generate_saml_response(user, auth_settings) - auth_settings = saml_settings( - overrides: { name_identifier_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL }, - ) - saml_get_auth(auth_settings) + expect(response.status).to eq(200) - expect(controller).to render_template('saml_idp/auth/error') - expect(response.status).to eq(400) - expect(response.body).to include(t('errors.messages.unauthorized_nameid_format')) + expect(name_id.attributes['Format'].value). + to eq(Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT) - analytics_hash = { - success: false, - errors: { nameid_format: [t('errors.messages.unauthorized_nameid_format')] }, - error_details: { nameid_format: { unauthorized_nameid_format: true } }, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - service_provider: 'http://localhost:3000', - request_signed: true, - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - matching_cert_serial: saml_test_sp_cert_serial, - } + expect(name_id.children.first.to_s).to eq(user.id.to_s) + end + end + end - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + context 'when the NameID format is missing' do + let(:name_identifier_format) { nil } + + context 'when the service provider is not configured with use_legacy_name_id_behavior' do + let(:use_legacy_name_id_behavior) { false } + + it_behaves_like 'sends the UUID', nil + end + context 'when the service provider is configured with use_legacy_name_id_behavior' do + let(:use_legacy_name_id_behavior) { true } + it_behaves_like 'sends the UUID', nil + end end - end - context 'service provider sends unsupported NameID format' do - let(:user) { create(:user, :fully_registered) } - let(:xmldoc) { SamlResponseDoc.new('controller', 'response_assertion', response) } - let(:subject) { xmldoc.subject_nodeset[0] } - let(:name_id) { subject.at('//ds:NameID', ds: Saml::XML::Namespaces::ASSERTION) } + context 'when the NameID format is "persistent"' do + let(:name_identifier_format) { Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT } - before do - stub_analytics - allow(@analytics).to receive(:track_event) + it_behaves_like 'sends the UUID', Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT end - it 'sends the appropriate identifier for non-email NameID SPs' do - auth_settings = saml_settings(overrides: { name_identifier_format: nil }) - auth_settings.name_identifier_format = - 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' - service_provider = build(:service_provider, issuer: auth_settings.issuer) - IdentityLinker.new(user, service_provider).link_identity - user.identities.last.update!(verified_attributes: ['email']) - generate_saml_response(user, auth_settings) + context 'when the NameID format is "email"' do + let(:name_identifier_format) { Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL } - expect(response.status).to eq(200) + context 'when the service provider is not allowed to use email' do + let(:email_allowed) { false } - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } + it_behaves_like 'returns an unauthorized nameid error', + Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL + end - expect(name_id.children.first.to_s).to eq(user.agency_identities.last.uuid) - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + context 'when the service provider is allowed to use email' do + let(:email_allowed) { true } + it_behaves_like 'sends the email', Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL + end end - it 'sends the appropriate identifier for email NameID SPs' do - auth_settings = saml_settings(overrides: { name_identifier_format: nil }) - auth_settings.name_identifier_format = - 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' - service_provider = ServiceProvider.find_by(issuer: auth_settings.issuer) - service_provider.update!(email_nameid_format_allowed: true) - IdentityLinker.new(user, service_provider).link_identity - user.identities.last.update!(verified_attributes: ['email']) - generate_saml_response(user, auth_settings) + context 'when the NameID format is an unsupported value' do + let(:name_identifier_format) { 'urn:oasis:names:tc:SAML:1.1:nameid-format:transient' } + let(:use_legacy_name_id_behavior) { nil } - expect(response.status).to eq(200) + context 'when the service provider is not configured with use_legacy_name_id_behavior' do + # This should always return an error. An aborted attempt was made to fix this + # with 30cb0f374 + let(:use_legacy_name_id_behavior) { false } - analytics_hash = { - success: true, - errors: {}, - nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_EMAIL, - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: auth_settings.issuer, - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } + context 'when the service provider is not allowed to use email' do + let(:email_allowed) { false } - expect(name_id.children.first.to_s).to eq(user.email_addresses.first.email) - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) - end + it_behaves_like 'sends the UUID', 'urn:oasis:names:tc:SAML:1.1:nameid-format:transient' + end - it 'sends the old user ID for legacy SPS' do - auth_settings = saml_settings(overrides: { name_identifier_format: nil }) - auth_settings.name_identifier_format = - 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' - service_provider = ServiceProvider.find_by(issuer: auth_settings.issuer) - service_provider.update!(use_legacy_name_id_behavior: true) - IdentityLinker.new(user, service_provider).link_identity - user.identities.last.update!(verified_attributes: ['email']) - generate_saml_response(user, auth_settings) + context 'when the service provider is allowed to use email' do + let(:email_allowed) { true } - expect(response.status).to eq(200) + it_behaves_like 'sends the email', 'urn:oasis:names:tc:SAML:1.1:nameid-format:transient' + end + end - analytics_hash = { - success: true, - errors: {}, - nameid_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', - authn_context: request_authn_contexts, - authn_context_comparison: 'exact', - requested_ial: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - service_provider: 'http://localhost:3000', - endpoint: "/api/saml/auth#{path_year}", - idv: false, - finish_profile: false, - request_signed: true, - matching_cert_serial: saml_test_sp_cert_serial, - } + context 'when the service provider is configured with use_legacy_name_id_behavior' do + let(:use_legacy_name_id_behavior) { true } - expect(name_id.children.first.to_s).to eq(user.id.to_s) - expect(@analytics).to have_received(:track_event). - with('SAML Auth', analytics_hash) + it 'sends the id, not the UUID' do + generate_saml_response(user, auth_settings) + + expect(response.status).to eq(200) + + expect(name_id.attributes['Format'].value). + to eq(Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT) + + expect(name_id.children.first.to_s).to eq(user.id.to_s) + end + end end end @@ -2341,6 +2264,7 @@ def name_id_version(format_urn) analytics_hash = { success: true, errors: {}, + error_details: nil, nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, authn_context: [ Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, @@ -2369,7 +2293,7 @@ def name_id_version(format_urn) user_fully_authenticated: true, }) expect(@analytics).to receive(:track_event). - with('SAML Auth', analytics_hash) + with('SAML Auth', hash_including(analytics_hash)) get :auth, params: { path_year: path_year } end @@ -2396,6 +2320,7 @@ def stub_requested_attributes analytics_hash = { success: true, errors: {}, + error_details: nil, nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, authn_context: request_authn_contexts, authn_context_comparison: 'exact', @@ -2417,7 +2342,10 @@ def stub_requested_attributes force_authn: false, user_fully_authenticated: true, }) - expect(@analytics).to receive(:track_event).with('SAML Auth', analytics_hash) + expect(@analytics).to receive(:track_event).with( + 'SAML Auth', + hash_including(analytics_hash), + ) expect(@analytics).to receive(:track_event).with( 'SP redirect initiated', ial: 1, @@ -2447,6 +2375,7 @@ def stub_requested_attributes analytics_hash = { success: true, errors: {}, + error_details: nil, nameid_format: Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT, authn_context: request_authn_contexts, authn_context_comparison: 'exact', @@ -2468,7 +2397,10 @@ def stub_requested_attributes force_authn: false, user_fully_authenticated: true, }) - expect(@analytics).to receive(:track_event).with('SAML Auth', analytics_hash) + expect(@analytics).to receive(:track_event).with( + 'SAML Auth', + hash_including(analytics_hash), + ) expect(@analytics).to receive(:track_event).with( 'SP redirect initiated', ial: 1, diff --git a/spec/controllers/sign_up/passwords_controller_spec.rb b/spec/controllers/sign_up/passwords_controller_spec.rb index 13ab19211bd..80597c6f9a4 100644 --- a/spec/controllers/sign_up/passwords_controller_spec.rb +++ b/spec/controllers/sign_up/passwords_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe SignUp::PasswordsController, allowed_extra_analytics: [:*] do +RSpec.describe SignUp::PasswordsController do let(:token) { 'new token' } describe '#create' do @@ -24,6 +24,7 @@ { success: true, errors: {}, + error_details: nil, user_id: user.uuid, } end diff --git a/spec/controllers/two_factor_authentication/options_controller_spec.rb b/spec/controllers/two_factor_authentication/options_controller_spec.rb index 6159c46e7ef..14c9c5d8368 100644 --- a/spec/controllers/two_factor_authentication/options_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/options_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe TwoFactorAuthentication::OptionsController, allowed_extra_analytics: [:*] do +RSpec.describe TwoFactorAuthentication::OptionsController do describe '#index' do it 'renders the page' do sign_in_before_2fa @@ -80,16 +80,15 @@ stub_sign_in_before_2fa stub_analytics - result = { + post :create, params: { two_factor_options_form: { selection: 'sms' } } + + expect(@analytics).to have_logged_event( + 'Multi-Factor Authentication: option list', selection: 'sms', success: true, errors: {}, - } - - expect(@analytics).to receive(:track_event). - with('Multi-Factor Authentication: option list', result) - - post :create, params: { two_factor_options_form: { selection: 'sms' } } + error_details: nil, + ) end end end diff --git a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb index dc18bd26fa1..b87fad16fbf 100644 --- a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb @@ -469,6 +469,7 @@ properties = { success: true, errors: nil, + error_details: nil, confirmation_for_add_phone: true, context: 'confirmation', multi_factor_auth_method: 'sms', @@ -620,6 +621,7 @@ properties = { success: true, errors: nil, + error_details: nil, context: 'confirmation', multi_factor_auth_method: 'sms', multi_factor_auth_method_created_at: nil, diff --git a/spec/controllers/users/edit_phone_controller_spec.rb b/spec/controllers/users/edit_phone_controller_spec.rb index c9df63102fd..6396421d5fb 100644 --- a/spec/controllers/users/edit_phone_controller_spec.rb +++ b/spec/controllers/users/edit_phone_controller_spec.rb @@ -15,6 +15,7 @@ attributes = { success: true, errors: {}, + error_details: nil, delivery_preference: 'voice', make_default_number: true, phone_configuration_id: phone_configuration.id, diff --git a/spec/controllers/users/email_language_controller_spec.rb b/spec/controllers/users/email_language_controller_spec.rb index eac8d20e0fd..1aaccdaf48b 100644 --- a/spec/controllers/users/email_language_controller_spec.rb +++ b/spec/controllers/users/email_language_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Users::EmailLanguageController, allowed_extra_analytics: [:*] do +RSpec.describe Users::EmailLanguageController do describe 'before_actions' do it 'includes appropriate before_actions' do expect(subject).to have_actions( diff --git a/spec/controllers/users/emails_controller_spec.rb b/spec/controllers/users/emails_controller_spec.rb index 75159965097..8906fe84f20 100644 --- a/spec/controllers/users/emails_controller_spec.rb +++ b/spec/controllers/users/emails_controller_spec.rb @@ -57,7 +57,11 @@ expect(@analytics).to receive(:track_event).with( 'Add Email Requested', - { success: true, errors: {}, user_id: user.uuid, domain_name: email.split('@').last }, + success: true, + errors: {}, + error_details: nil, + user_id: user.uuid, + domain_name: email.split('@').last, ) expect(@analytics).to receive(:track_event).with( diff --git a/spec/controllers/users/passwords_controller_spec.rb b/spec/controllers/users/passwords_controller_spec.rb index 11979e97f11..548cb918a6d 100644 --- a/spec/controllers/users/passwords_controller_spec.rb +++ b/spec/controllers/users/passwords_controller_spec.rb @@ -30,6 +30,7 @@ 'Password Changed', success: true, errors: {}, + error_details: nil, pending_profile_present: false, active_profile_present: false, user_id: subject.current_user.uuid, diff --git a/spec/controllers/users/phone_setup_controller_spec.rb b/spec/controllers/users/phone_setup_controller_spec.rb index 9c372dfc2c7..b5b1d462b76 100644 --- a/spec/controllers/users/phone_setup_controller_spec.rb +++ b/spec/controllers/users/phone_setup_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Users::PhoneSetupController, allowed_extra_analytics: [:*] do +RSpec.describe Users::PhoneSetupController do let(:mfa_selections) { ['voice'] } before do allow(IdentityConfig.store).to receive(:phone_service_check).and_return(true) @@ -151,6 +151,7 @@ result = { success: true, errors: {}, + error_details: nil, otp_delivery_preference: 'voice', area_code: '703', carrier: 'Test Mobile Carrier', @@ -191,6 +192,7 @@ result = { success: true, errors: {}, + error_details: nil, otp_delivery_preference: 'sms', area_code: '703', carrier: 'Test Mobile Carrier', @@ -230,6 +232,7 @@ result = { success: true, errors: {}, + error_details: nil, otp_delivery_preference: 'sms', area_code: '703', carrier: 'Test Mobile Carrier', diff --git a/spec/controllers/users/rules_of_use_controller_spec.rb b/spec/controllers/users/rules_of_use_controller_spec.rb index 9877d11e30b..012635be0b7 100644 --- a/spec/controllers/users/rules_of_use_controller_spec.rb +++ b/spec/controllers/users/rules_of_use_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Users::RulesOfUseController, allowed_extra_analytics: [:*] do +RSpec.describe Users::RulesOfUseController do let(:rules_of_use_updated_at) { 1.day.ago } let(:accepted_terms_at) { nil } let(:user) { create(:user, :fully_registered, accepted_terms_at: accepted_terms_at) } diff --git a/spec/controllers/users/totp_setup_controller_spec.rb b/spec/controllers/users/totp_setup_controller_spec.rb index 3ab67453d88..d3ff1a139c2 100644 --- a/spec/controllers/users/totp_setup_controller_spec.rb +++ b/spec/controllers/users/totp_setup_controller_spec.rb @@ -110,6 +110,7 @@ result = { success: false, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: nil, @@ -142,6 +143,7 @@ result = { success: true, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: next_auth_app_id, @@ -175,6 +177,7 @@ result = { success: false, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: nil, @@ -242,6 +245,7 @@ result = { success: false, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: nil, @@ -276,6 +280,7 @@ result = { success: true, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: next_auth_app_id, @@ -298,6 +303,7 @@ result = { success: true, errors: {}, + error_details: nil, totp_secret_present: true, multi_factor_auth_method: 'totp', auth_app_configuration_id: next_auth_app_id, @@ -329,6 +335,7 @@ result = { success: false, errors: {}, + error_details: nil, totp_secret_present: false, multi_factor_auth_method: 'totp', auth_app_configuration_id: nil, diff --git a/spec/controllers/users/two_factor_authentication_controller_spec.rb b/spec/controllers/users/two_factor_authentication_controller_spec.rb index 3e34dd01523..729e3460001 100644 --- a/spec/controllers/users/two_factor_authentication_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_controller_spec.rb @@ -326,6 +326,7 @@ def index analytics_hash = { success: true, errors: {}, + error_details: nil, **otp_preference_sms, resend: true, context: 'authentication', @@ -494,6 +495,7 @@ def index analytics_hash = { success: true, errors: {}, + error_details: nil, otp_delivery_preference: 'voice', resend: false, context: 'authentication', diff --git a/spec/controllers/users/verify_personal_key_controller_spec.rb b/spec/controllers/users/verify_personal_key_controller_spec.rb index 06ebf314b71..06720ad6d67 100644 --- a/spec/controllers/users/verify_personal_key_controller_spec.rb +++ b/spec/controllers/users/verify_personal_key_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Users::VerifyPersonalKeyController, allowed_extra_analytics: [:*] do +RSpec.describe Users::VerifyPersonalKeyController do let(:user) { create(:user, personal_key: personal_key) } let!(:profiles) { [] } let(:personal_key) { 'key' } @@ -107,6 +107,7 @@ expect(@analytics).to receive(:track_event).with( 'Personal key reactivation: Personal key form submitted', errors: {}, + error_details: nil, success: true, pii_like_keypaths: pii_like_keypaths_errors, ).once diff --git a/spec/controllers/users/webauthn_setup_controller_spec.rb b/spec/controllers/users/webauthn_setup_controller_spec.rb index fe5ccd85d8d..b0045caa5a0 100644 --- a/spec/controllers/users/webauthn_setup_controller_spec.rb +++ b/spec/controllers/users/webauthn_setup_controller_spec.rb @@ -105,6 +105,7 @@ multi_factor_auth_method: 'webauthn', success: true, errors: {}, + error_details: nil, in_account_creation_flow: false, authenticator_data_flags: { up: true, @@ -244,6 +245,7 @@ { enabled_mfa_methods_count: 1, errors: {}, + error_details: nil, in_account_creation_flow: true, mfa_method_counts: { webauthn: 1 }, multi_factor_auth_method: 'webauthn', @@ -311,6 +313,7 @@ { enabled_mfa_methods_count: 1, errors: {}, + error_details: nil, in_account_creation_flow: true, mfa_method_counts: { webauthn_platform: 1 }, multi_factor_auth_method: 'webauthn_platform', diff --git a/spec/features/accessibility/user_pages_spec.rb b/spec/features/accessibility/user_pages_spec.rb index aacdf55a1f6..175161ed927 100644 --- a/spec/features/accessibility/user_pages_spec.rb +++ b/spec/features/accessibility/user_pages_spec.rb @@ -1,8 +1,7 @@ require 'rails_helper' require 'axe-rspec' -RSpec.feature 'Accessibility on pages that require authentication', :js, - allowed_extra_analytics: [:*] do +RSpec.feature 'Accessibility on pages that require authentication', :js do scenario 'user registration page' do email = 'test@example.com' sign_up_with(email) diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index c0debcc985c..e6652de97cb 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -74,11 +74,11 @@ width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: kind_of(String), acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload form submitted' => { - success: true, errors: {}, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean + success: true, errors: {}, error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload vendor submitted' => hash_including(success: true, flow_path: 'standard', attention_with_barcode: false, doc_auth_result: 'Passed', liveness_checking_required: boolean), 'IdV: doc auth image upload vendor pii validation' => { - success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' + success: true, errors: {}, error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, selfie_check_required: boolean, liveness_checking_required: boolean @@ -105,17 +105,17 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation form' => { - success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', + success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, '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, + success: true, errors: {}, error_details: nil, 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, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, '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, + success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -124,7 +124,7 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: phone confirmation otp submitted' => { - success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, + success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -200,11 +200,11 @@ width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'hybrid', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: kind_of(String), acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload form submitted' => { - success: true, errors: {}, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'hybrid', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean + success: true, errors: {}, error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'hybrid', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload vendor submitted' => hash_including(success: true, flow_path: 'hybrid', attention_with_barcode: false, doc_auth_result: 'Passed', liveness_checking_required: boolean), 'IdV: doc auth image upload vendor pii validation' => { - success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'hybrid', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' + success: true, errors: {}, error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'hybrid', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { success: true, errors: {}, flow_path: 'hybrid', step: 'document_capture', acuant_sdk_upgrade_ab_test_bucket: :default, analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean @@ -231,17 +231,17 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation form' => { - success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', + success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, '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: true, area_code: '202', country_code: 'US', phone_fingerprint: anything, + success: true, errors: {}, error_details: nil, 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: true, area_code: '202', country_code: 'US', phone_fingerprint: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, '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, + success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -250,7 +250,7 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: phone confirmation otp submitted' => { - success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, + success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -323,11 +323,11 @@ width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: kind_of(String), acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload form submitted' => { - success: true, errors: {}, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean + success: true, errors: {}, error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload vendor submitted' => hash_including(success: true, flow_path: 'standard', attention_with_barcode: false, doc_auth_result: 'Passed', liveness_checking_required: boolean), 'IdV: doc auth image upload vendor pii validation' => { - success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' + success: true, errors: {}, error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: boolean @@ -420,7 +420,7 @@ width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: kind_of(String), acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload form submitted' => { - success: true, errors: {}, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean + success: true, errors: {}, error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload vendor submitted' => hash_including(success: true, flow_path: 'standard', attention_with_barcode: true, doc_auth_result: 'Attention', liveness_checking_required: boolean), 'IdV: verify in person troubleshooting option clicked' => { @@ -442,7 +442,7 @@ step: 'state_id', flow_path: 'standard', step_count: 1, analytics_id: 'In Person Proofing', opted_in_to_in_person_proofing: nil }, 'IdV: in person proofing state_id submitted' => { - success: true, flow_path: 'standard', step: 'state_id', step_count: 1, analytics_id: 'In Person Proofing', errors: {}, same_address_as_id: false, opted_in_to_in_person_proofing: nil + success: true, flow_path: 'standard', step: 'state_id', step_count: 1, analytics_id: 'In Person Proofing', errors: {}, error_details: nil, same_address_as_id: false, opted_in_to_in_person_proofing: nil }, 'IdV: in person proofing address visited' => { step: 'address', flow_path: 'standard', analytics_id: 'In Person Proofing', same_address_as_id: false, opted_in_to_in_person_proofing: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil @@ -467,17 +467,17 @@ proofing_results: { exception: nil, timed_out: false, threatmetrix_review_status: 'pass', context: { device_profiling_adjudication_reason: 'device_profiling_result_pass', resolution_adjudication_reason: 'pass_resolution_and_state_id', should_proof_state_id: true, stages: { resolution: { success: true, errors: {}, exception: nil, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', can_pass_with_additional_verification: false, attributes_requiring_additional_verification: [], vendor_name: 'ResolutionMock', vendor_workflow: nil }, residential_address: { errors: {}, exception: nil, reference: 'aaa-bbb-ccc', success: true, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', can_pass_with_additional_verification: false, attributes_requiring_additional_verification: [], vendor_name: 'ResolutionMock', vendor_workflow: nil }, state_id: { success: true, errors: {}, exception: nil, mva_exception: nil, requested_attributes: {}, timed_out: false, transaction_id: 'state-id-mock-transaction-id-456', vendor_name: 'StateIdMock', verified_attributes: [], state: 'MT', state_id_jurisdiction: 'ND', state_id_number: '#############' }, threatmetrix: threatmetrix_response } } } }, 'IdV: phone confirmation form' => { - success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', + success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } }, '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, + success: true, errors: {}, error_details: nil, 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, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } }, '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, + success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } }, @@ -486,7 +486,7 @@ proofing_components: { address_check: 'lexis_nexis_address', document_check: 'usps', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', source_check: 'aamva' } }, 'IdV: phone confirmation otp submitted' => { - success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, + success: true, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'usps', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -571,11 +571,11 @@ width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: kind_of(String), acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil, liveness_checking_required: boolean }, 'IdV: doc auth image upload form submitted' => { - success: true, errors: {}, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean + success: true, errors: {}, error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: user.uuid, flow_path: 'standard', front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean }, 'IdV: doc auth image upload vendor submitted' => hash_including(success: true, flow_path: 'standard', attention_with_barcode: false, doc_auth_result: 'Passed', liveness_checking_required: boolean), 'IdV: doc auth image upload vendor pii validation' => { - success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' + success: true, errors: {}, error_details: nil, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {}, id_issued_status: 'present', id_expiration_status: 'present' }, 'IdV: doc auth document_capture submitted' => { success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: nil, acuant_sdk_upgrade_ab_test_bucket: :default, analytics_id: 'Doc Auth', selfie_check_required: boolean, liveness_checking_required: true @@ -605,17 +605,17 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation form' => { - success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, otp_delivery_preference: 'sms', + success: true, errors: {}, error_details: nil, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, otp_delivery_preference: 'sms', active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, '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, + success: true, errors: {}, error_details: nil, 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, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, '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, + success: true, otp_delivery_preference: :sms, country_code: 'US', area_code: '202', adapter: :test, errors: {}, error_details: nil, phone_fingerprint: anything, rate_limit_exceeded: false, telephony_response: anything, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, @@ -624,7 +624,7 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: phone confirmation otp submitted' => { - success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, + success: true, acuant_sdk_upgrade_ab_test_bucket: :default, skip_hybrid_handoff: anything, code_expired: false, code_matches: true, otp_delivery_preference: :sms, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, error_details: nil, active_profile_idv_level: nil, pending_profile_idv_level: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, diff --git a/spec/features/new_device_tracking_spec.rb b/spec/features/new_device_tracking_spec.rb index 272d45e5dca..b5e36214643 100644 --- a/spec/features/new_device_tracking_spec.rb +++ b/spec/features/new_device_tracking_spec.rb @@ -108,13 +108,23 @@ travel_to 38.minutes.from_now do visit root_url expect(current_path).to eq(new_user_session_path) - sign_in_live_with_2fa(user) + + # Regression: LG-13221: Ensure that the successful authentication email lists failed MFA. + sign_in_user(user) + fill_in t('components.one_time_code_input.label'), with: '000000' + click_submit_default + fill_in_code_with_last_phone_otp + click_submit_default + open_last_email email_page = Capybara::Node::Simple.new(current_email.default_part_body) - expect(email_page).to have_css('.usa-table td.font-family-mono', count: 2) + expect(email_page).to have_css('.usa-table td.font-family-mono', count: 3) expect(email_page).to have_content( t('user_mailer.new_device_sign_in_attempts.events.sign_in_before_2fa'), ) + expect(email_page).to have_content( + t('user_mailer.new_device_sign_in_attempts.events.sign_in_unsuccessful_2fa'), + ) expect(email_page).to have_content( t('user_mailer.new_device_sign_in_attempts.events.sign_in_after_2fa'), ) diff --git a/spec/features/openid_connect/redirect_uri_validation_spec.rb b/spec/features/openid_connect/redirect_uri_validation_spec.rb index 17f76594690..9815881d9d1 100644 --- a/spec/features/openid_connect/redirect_uri_validation_spec.rb +++ b/spec/features/openid_connect/redirect_uri_validation_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe 'redirect_uri validation', allowed_extra_analytics: [:*] do +RSpec.describe 'redirect_uri validation' do include OidcAuthHelper context 'when the redirect_uri in the request does not match one that is registered' do diff --git a/spec/features/reports/authorization_count_spec.rb b/spec/features/reports/authorization_count_spec.rb index 44be18cd863..a82067f5d68 100644 --- a/spec/features/reports/authorization_count_spec.rb +++ b/spec/features/reports/authorization_count_spec.rb @@ -33,7 +33,7 @@ def visit_idp_from_ial2_saml_sp(issuer:) ) end -RSpec.describe 'authorization count', allowed_extra_analytics: [:*] do +RSpec.describe 'authorization count' do include IdvFromSpHelper include OidcAuthHelper include DocAuthHelper diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index e6b18698a01..0ad102e20f4 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -122,6 +122,7 @@ 'Multi-Factor Authentication Setup', success: true, errors: nil, + error_details: nil, multi_factor_auth_method: 'backup_codes', in_account_creation_flow: true, enabled_mfa_methods_count: 2, diff --git a/spec/features/visitors/password_recovery_spec.rb b/spec/features/visitors/password_recovery_spec.rb index 5f29af416ab..dfc4c00878a 100644 --- a/spec/features/visitors/password_recovery_spec.rb +++ b/spec/features/visitors/password_recovery_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.feature 'Password Recovery', allowed_extra_analytics: [:*] do +RSpec.feature 'Password Recovery' do include IdvHelper include PersonalKeyHelper include SamlAuthHelper diff --git a/spec/features/visitors/set_password_spec.rb b/spec/features/visitors/set_password_spec.rb index a0f92e90f2e..6c7edfe9d99 100644 --- a/spec/features/visitors/set_password_spec.rb +++ b/spec/features/visitors/set_password_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.feature 'Visitor sets password during signup', allowed_extra_analytics: [:*] do +RSpec.feature 'Visitor sets password during signup' do scenario 'visitor is redirected back to password form when password is blank' do create(:user, :unconfirmed) confirm_last_user diff --git a/spec/forms/idv/api_image_upload_form_spec.rb b/spec/forms/idv/api_image_upload_form_spec.rb index 45c3bb180cd..f1b69b835e5 100644 --- a/spec/forms/idv/api_image_upload_form_spec.rb +++ b/spec/forms/idv/api_image_upload_form_spec.rb @@ -169,6 +169,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: document_capture_session.user.uuid, @@ -267,6 +268,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: document_capture_session.user.uuid, @@ -399,6 +401,7 @@ 'IdV: doc auth image upload form submitted', success: true, errors: {}, + error_details: nil, submit_attempts: 1, remaining_submit_attempts: 3, user_id: document_capture_session.user.uuid, diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index ef708925c75..3c789af7c16 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -148,50 +148,6 @@ class BaseTask { key: 'user_mailer.new_device_sign_in_before_2fa.info_p1_html.one', locales: %i[zh] }, { key: 'user_mailer.new_device_sign_in_before_2fa.info_p1_html.other', locales: %i[zh] }, { key: 'user_mailer.new_device_sign_in_before_2fa.info_p1_html.zero', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.bad_client_id', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.invalid_verified_within_duration.one', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.invalid_verified_within_duration.other', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.invalid_verified_within_format', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.missing_ial', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.no_auth', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.no_valid_acr_values', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.no_valid_scope', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.prompt_invalid', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.redirect_uri_invalid', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.redirect_uri_no_match', locales: %i[zh] }, - { key: 'openid_connect.authorization.errors.unauthorized_scope', locales: %i[zh] }, - { key: 'openid_connect.logout.confirm', locales: %i[zh] }, - { key: 'openid_connect.logout.deny', locales: %i[zh] }, - { key: 'openid_connect.logout.errors.client_id_invalid', locales: %i[zh] }, - { key: 'openid_connect.logout.errors.client_id_missing', locales: %i[zh] }, - { key: 'openid_connect.logout.errors.id_token_hint', locales: %i[zh] }, - { key: 'openid_connect.logout.errors.id_token_hint_present', locales: %i[zh] }, - { key: 'openid_connect.logout.errors.no_client_id_or_id_token_hint', locales: %i[zh] }, - { key: 'openid_connect.logout.heading', locales: %i[zh] }, - { key: 'openid_connect.logout.heading_with_sp', locales: %i[zh] }, - { key: 'openid_connect.token.errors.expired_code', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_aud', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_authentication', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_code', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_code_verifier', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_iat', locales: %i[zh] }, - { key: 'openid_connect.token.errors.invalid_signature', locales: %i[zh] }, - { key: 'openid_connect.user_info.errors.malformed_authorization', locales: %i[zh] }, - { key: 'openid_connect.user_info.errors.no_authorization', locales: %i[zh] }, - { key: 'openid_connect.user_info.errors.not_found', locales: %i[zh] }, - { key: 'risc.security_event.errors.alg_unsupported', locales: %i[zh] }, - { key: 'risc.security_event.errors.aud_invalid', locales: %i[zh] }, - { key: 'risc.security_event.errors.event_type_missing', locales: %i[zh] }, - { key: 'risc.security_event.errors.event_type_unsupported', locales: %i[zh] }, - { key: 'risc.security_event.errors.exp_present', locales: %i[zh] }, - { key: 'risc.security_event.errors.jti_not_unique', locales: %i[zh] }, - { key: 'risc.security_event.errors.jti_required', locales: %i[zh] }, - { key: 'risc.security_event.errors.jwt_could_not_parse', locales: %i[zh] }, - { key: 'risc.security_event.errors.no_public_key', locales: %i[zh] }, - { key: 'risc.security_event.errors.sub_not_found', locales: %i[zh] }, - { key: 'risc.security_event.errors.sub_unsupported', locales: %i[zh] }, - { key: 'risc.security_event.errors.subject_type_unsupported', locales: %i[zh] }, - { key: 'risc.security_event.errors.typ_error', locales: %i[zh] }, ].freeze # rubocop:enable Layout/LineLength diff --git a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb index 31b075152ff..21332e4c4d1 100644 --- a/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb +++ b/spec/jobs/reports/combined_invoice_supplement_report_v2_spec.rb @@ -72,11 +72,11 @@ end let(:iaa1) { 'iaa1' } - let(:iaa1_range) { Date.new(2020, 4, 15)..Date.new(2021, 4, 14) } + let(:iaa1_range) { DateTime.new(2020, 4, 15).utc..DateTime.new(2021, 4, 14).utc } let(:inside_iaa1) { iaa1_range.begin + 1.day } let(:iaa2) { 'iaa2' } - let(:iaa2_range) { Date.new(2020, 9, 1)..Date.new(2021, 8, 30) } + let(:iaa2_range) { DateTime.new(2020, 9, 1).utc..DateTime.new(2021, 8, 30).utc } let(:inside_iaa2) { iaa2_range.begin + 1.day } describe '#perform' do @@ -89,6 +89,9 @@ let(:user1) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } + let(:user4) { create(:user) } + let(:user5) { create(:user) } + let(:user6) { create(:user) } before do iaa_order1.integrations << build_integration( @@ -125,7 +128,7 @@ issuer: iaa2_sp1.issuer, requested_at: inside_iaa2, returned_at: inside_iaa2, - profile_verified_at: '2020-01-01 00:00:00', + profile_verified_at: DateTime.new(2020, 1, 1).utc, billable: true, ) @@ -138,7 +141,33 @@ issuer: iaa2_sp2.issuer, requested_at: inside_iaa2, returned_at: inside_iaa2, - profile_verified_at: '2019-01-01 00:00:00', + profile_verified_at: DateTime.new(2019, 1, 1).utc, + billable: true, + ) + end + + # 1 unique user in month 1 at IAA 2 sp 1 @ IAL 2 with profile age unknown + create( + :sp_return_log, + user_id: user4.id, + ial: 2, + issuer: iaa2_sp1.issuer, + requested_at: inside_iaa2, + returned_at: inside_iaa2, + profile_verified_at: nil, + billable: true, + ) + + # 2 new unique users in month 1 at IAA 1 sp @ IAL 2 with profile age 2 + [user5, user6].each do |user| + create( + :sp_return_log, + user_id: user.id, + ial: 2, + issuer: iaa1_sp.issuer, + requested_at: inside_iaa1, + returned_at: inside_iaa1, + profile_verified_at: DateTime.new(2018, 6, 1).utc, billable: true, ) end @@ -152,7 +181,7 @@ aggregate_failures do row = csv.find { |r| r['issuer'] == iaa1_sp.issuer } expect(row['iaa_order_number']).to eq('gtc1234-0001') - expect(row['partner']).to eq(nil) + expect(row['partner']).to eq(partner_account1.requesting_agency) expect(row['iaa_start_date']).to eq('2020-04-15') expect(row['iaa_end_date']).to eq('2021-04-14') @@ -163,30 +192,30 @@ expect(row['year_month_readable']).to eq('April 2020') expect(row['iaa_ial1_unique_users'].to_i).to eq(1) - expect(row['iaa_ial2_unique_users'].to_i).to eq(0) - expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(1) + expect(row['iaa_ial2_unique_users'].to_i).to eq(2) + expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(3) expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(0) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(1) - expect(row['issuer_ial2_total_auth_count'].to_i).to eq(0) - expect(row['issuer_ial1_plus_2_total_auth_count'].to_i).to eq(1) + expect(row['issuer_ial2_total_auth_count'].to_i).to eq(2) + expect(row['issuer_ial1_plus_2_total_auth_count'].to_i).to eq(3) expect(row['issuer_ial1_unique_users'].to_i).to eq(1) - expect(row['issuer_ial2_unique_users'].to_i).to eq(0) - expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(1) + expect(row['issuer_ial2_unique_users'].to_i).to eq(2) + expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(3) expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(2) expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end aggregate_failures do @@ -204,30 +233,30 @@ expect(row['year_month_readable']).to eq('September 2020') expect(row['iaa_ial1_unique_users'].to_i).to eq(0) - expect(row['iaa_ial2_unique_users'].to_i).to eq(3) - expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(3) + expect(row['iaa_ial2_unique_users'].to_i).to eq(4) + expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(4) expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(1) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) - expect(row['issuer_ial2_total_auth_count'].to_i).to eq(1) - expect(row['issuer_ial1_plus_2_total_auth_count'].to_i).to eq(1) + expect(row['issuer_ial2_total_auth_count'].to_i).to eq(2) + expect(row['issuer_ial1_plus_2_total_auth_count'].to_i).to eq(2) expect(row['issuer_ial1_unique_users'].to_i).to eq(0) - expect(row['issuer_ial2_unique_users'].to_i).to eq(1) - expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(1) + expect(row['issuer_ial2_unique_users'].to_i).to eq(2) + expect(row['issuer_ial1_plus_2_unique_users'].to_i).to eq(2) expect(row['issuer_ial2_new_unique_users_year1'].to_i).to eq(1) expect(row['issuer_ial2_new_unique_users_year2'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year3'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(1) end aggregate_failures do @@ -245,15 +274,15 @@ expect(row['year_month_readable']).to eq('September 2020') expect(row['iaa_ial1_unique_users'].to_i).to eq(0) - expect(row['iaa_ial2_unique_users'].to_i).to eq(3) - expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(3) + expect(row['iaa_ial2_unique_users'].to_i).to eq(4) + expect(row['iaa_ial1_plus_2_unique_users'].to_i).to eq(4) expect(row['partner_ial2_new_unique_users_year1'].to_i).to eq(1) expect(row['partner_ial2_new_unique_users_year2'].to_i).to eq(2) expect(row['partner_ial2_new_unique_users_year3'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['partner_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['partner_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['partner_ial2_new_unique_users_unknown'].to_i).to eq(1) expect(row['issuer_ial1_total_auth_count'].to_i).to eq(0) expect(row['issuer_ial2_total_auth_count'].to_i).to eq(2) @@ -268,7 +297,7 @@ expect(row['issuer_ial2_new_unique_users_year4'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year5'].to_i).to eq(0) expect(row['issuer_ial2_new_unique_users_year_greater_than_5'].to_i).to eq(0) - expect(row['issuer_ial2_new_unique_users_year_unknown'].to_i).to eq(0) + expect(row['issuer_ial2_new_unique_users_unknown'].to_i).to eq(0) end end end diff --git a/spec/jobs/resolution_proofing_job_spec.rb b/spec/jobs/resolution_proofing_job_spec.rb index 602b8ea3a90..f6d5bd89db2 100644 --- a/spec/jobs/resolution_proofing_job_spec.rb +++ b/spec/jobs/resolution_proofing_job_spec.rb @@ -13,6 +13,7 @@ let(:should_proof_state_id) { true } let(:trace_id) { SecureRandom.uuid } let(:user) { create(:user, :fully_registered) } + let(:service_provider) { create(:service_provider, app_id: 'fake-app-id') } let(:request_ip) { Faker::Internet.ip_v4_address } let(:threatmetrix_session_id) { SecureRandom.uuid } let(:proofing_device_profiling) { :enabled } @@ -38,6 +39,7 @@ encrypted_arguments: encrypted_arguments, trace_id: trace_id, user_id: user.id, + service_provider_issuer: service_provider.issuer, threatmetrix_session_id: threatmetrix_session_id, request_ip: request_ip, ipp_enrollment_in_progress: ipp_enrollment_in_progress, @@ -498,6 +500,24 @@ end end + it 'determines the UUID and UUID prefix and passes it to the downstream proofing vendors' do + uuid_info = { + uuid_prefix: service_provider.app_id, + uuid: user.uuid, + } + + stub_vendor_requests + + expect_any_instance_of(Proofing::LexisNexis::InstantVerify::Proofer).to receive(:proof). + with(hash_including(uuid_info)).and_call_original + + expect_any_instance_of(Proofing::Aamva::Proofer).to receive(:proof).with( + hash_including(uuid_info), + ).and_call_original + + perform + end + def stub_vendor_requests( instant_verify_response: LexisNexisFixtures.instant_verify_success_response_json, threatmetrix_response: LexisNexisFixtures.ddp_success_response_json, diff --git a/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb b/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb index a9734c22435..03bb5766990 100644 --- a/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb +++ b/spec/services/db/monthly_sp_auth_count/new_unique_monthly_user_counts_by_partner_spec.rb @@ -35,43 +35,46 @@ let(:partner_key) { 'DHS' } let(:issuers) { [issuer1, issuer2, issuer3] } - let(:partner_range) { Date.new(2020, 9, 15)..Date.new(2021, 9, 14) } - let(:inside_partial_month) { Date.new(2020, 9, 16) } - let(:inside_whole_month) { Date.new(2020, 10, 16) } + let(:partner_range) { DateTime.new(2020, 9, 15).utc..DateTime.new(2021, 9, 14).utc } + let(:inside_partial_month) { DateTime.new(2020, 9, 16).utc } + let(:inside_whole_month) { DateTime.new(2020, 10, 16).utc } let(:user1) { create(:user, profiles: [profile1a, profile1b]) } - let(:profile1a) { build(:profile, verified_at: '2015-09-16') } - let(:profile1b) { build(:profile, verified_at: '2020-09-16') } + let(:profile1a) { build(:profile, verified_at: DateTime.new(2015, 9, 17).utc) } + let(:profile1b) { build(:profile, verified_at: DateTime.new(2020, 9, 16).utc) } let(:user2) { create(:user, profiles: [profile2]) } - let(:profile2) { build(:profile, verified_at: '2018-12-25') } + let(:profile2) { build(:profile, verified_at: DateTime.new(2018, 12, 25).utc) } let(:user3) { create(:user, profiles: [profile3]) } - let(:profile3) { build(:profile, verified_at: '2019-11-10') } + let(:profile3) { build(:profile, verified_at: DateTime.new(2019, 11, 10).utc) } let(:user4) { create(:user, profiles: [profile4]) } - let(:profile4) { build(:profile, verified_at: '2020-03-01') } + let(:profile4) { build(:profile, verified_at: DateTime.new(2020, 3, 1).utc) } let(:user5) { create(:user, profiles: [profile5]) } - let(:profile5) { build(:profile, verified_at: '2019-04-17') } + let(:profile5) { build(:profile, verified_at: DateTime.new(2019, 4, 17).utc) } let(:user6) { create(:user, profiles: [profile6]) } - let(:profile6) { build(:profile, verified_at: '2018-09-15') } + let(:profile6) { build(:profile, verified_at: DateTime.new(2018, 9, 15).utc) } let(:user7) { create(:user, profiles: [profile7]) } - let(:profile7) { build(:profile, verified_at: '2017-02-15') } + let(:profile7) { build(:profile, verified_at: DateTime.new(2017, 2, 15).utc) } let(:user8) { create(:user, profiles: [profile8]) } - let(:profile8) { build(:profile, verified_at: '2016-03-20') } + let(:profile8) { build(:profile, verified_at: DateTime.new(2016, 3, 20).utc) } let(:user9) { create(:user, profiles: [profile9]) } - let(:profile9) { build(:profile, verified_at: '2012-12-15') } + let(:profile9) { build(:profile, verified_at: DateTime.new(2012, 12, 15).utc) } let(:user10) { create(:user, profiles: [profile10]) } let(:profile10) { build(:profile, verified_at: nil) } - let(:user11) { create(:user, profiles: [profile9]) } - let(:profile11) { build(:profile, verified_at: '2019-11-10') } + let(:user11) { create(:user, profiles: [profile11]) } + let(:profile11) { build(:profile, verified_at: DateTime.new(2019, 10, 1).utc) } + + let(:user12) { create(:user, profiles: [profile12]) } + let(:profile12) { build(:profile, verified_at: DateTime.new(2019, 10, 16).utc) } let(:issuer1) { 'issuer1' } let(:issuer2) { 'issuer2' } @@ -103,13 +106,13 @@ ial: 2, requested_at: inside_partial_month, returned_at: inside_partial_month, - profile_verified_at: user.profiles.map(&:verified_at).max, + profile_verified_at: user.profiles.map(&:verified_at).min, billable: true, ) end - # 5 new users in whole month proofed in year 1-5 - [user4, user5, user6, user7, user8].each do |user| + # 6 new users in partial month proofed in year 1-5 + [user4, user5, user6, user7, user8, user11].each do |user| create( :sp_return_log, user_id: user.id, @@ -188,6 +191,47 @@ end end + # 1 old user returns with a new profile age 2 inside whole month + [user11].each do |user| + 2.times do + create( + :sp_return_log, + user_id: user.id, + ial: 2, + issuer: issuer2, + requested_at: inside_whole_month, + returned_at: inside_whole_month, + profile_verified_at: user.profiles.map(&:verified_at).max, + billable: true, + ) + end + end + + # 1 new user signs in with profile age of 1 year and then signs in again later in same month + # with profile age of 2 years + [user12].each do |user| + create( + :sp_return_log, + user_id: user.id, + ial: 2, + issuer: issuer2, + requested_at: inside_whole_month, + returned_at: DateTime.new(2020, 10, 1).utc, + profile_verified_at: user.profiles.map(&:verified_at).max, + billable: true, + ) + create( + :sp_return_log, + user_id: user.id, + ial: 2, + issuer: issuer2, + requested_at: inside_whole_month, + returned_at: DateTime.new(2020, 10, 30).utc, + profile_verified_at: user.profiles.map(&:verified_at).max, + billable: true, + ) + end + # Outside analysis range # 1 new user returning outside the range of analysis [user11].each do |user, profile| @@ -197,8 +241,8 @@ user_id: user.id, ial: 2, issuer: issuer2, - requested_at: '2021-08-16', - returned_at: '2021-10-05', + requested_at: DateTime.new(2022, 8, 16).utc, + returned_at: DateTime.new(2022, 10, 5).utc, profile_verified_at: user.profiles[0].verified_at, billable: true, ) @@ -214,13 +258,13 @@ year_month: '202009', iaa_start_date: partner_range.begin.to_s, iaa_end_date: partner_range.end.to_s, - unique_users: 7, - new_unique_users: 7, + unique_user_proofed_events: 8, + new_unique_user_proofed_events: 8, partner_ial2_new_unique_users_year1: 2, partner_ial2_new_unique_users_year2: 2, partner_ial2_new_unique_users_year3: 1, partner_ial2_new_unique_users_year4: 1, - partner_ial2_new_unique_users_year5: 1, + partner_ial2_new_unique_users_year5: 2, partner_ial2_new_unique_users_year_greater_than_5: 0, partner_ial2_new_unique_users_unknown: 0, }, @@ -230,14 +274,14 @@ year_month: '202010', iaa_start_date: partner_range.begin.to_s, iaa_end_date: partner_range.end.to_s, - unique_users: 6, - new_unique_users: 3, - partner_ial2_new_unique_users_year1: 1, - partner_ial2_new_unique_users_year2: 0, + unique_user_proofed_events: 10, + new_unique_user_proofed_events: 8, + partner_ial2_new_unique_users_year1: 3, + partner_ial2_new_unique_users_year2: 2, partner_ial2_new_unique_users_year3: 0, partner_ial2_new_unique_users_year4: 0, partner_ial2_new_unique_users_year5: 0, - partner_ial2_new_unique_users_year_greater_than_5: 1, + partner_ial2_new_unique_users_year_greater_than_5: 2, partner_ial2_new_unique_users_unknown: 1, }, ] diff --git a/spec/services/idv/agent_spec.rb b/spec/services/idv/agent_spec.rb index 523b38445fe..2c042563c51 100644 --- a/spec/services/idv/agent_spec.rb +++ b/spec/services/idv/agent_spec.rb @@ -2,7 +2,7 @@ require 'ostruct' RSpec.describe Idv::Agent do - let(:user) { build(:user) } + let(:user) { create(:user) } let(:bad_phone) do Proofing::Mock::AddressMockClient::UNVERIFIABLE_PHONE_NUMBER diff --git a/spec/services/proofing/resolution/progressive_proofer_spec.rb b/spec/services/proofing/resolution/progressive_proofer_spec.rb index 88c0778bb60..268d30d2dfe 100644 --- a/spec/services/proofing/resolution/progressive_proofer_spec.rb +++ b/spec/services/proofing/resolution/progressive_proofer_spec.rb @@ -28,11 +28,8 @@ let(:proof_id_address_with_lexis_nexis_if_needed_value) { nil } let(:dcs_uuid) { SecureRandom.uuid } - let(:instance) do - instance = described_class.new - allow(instance).to receive(:user_can_pass_after_state_id_check?).and_call_original - instance - end + + subject(:progressive_proofer) { described_class.new } let(:state_id_address) do { @@ -91,9 +88,9 @@ def block_real_instant_verify_requests end before do - allow(instance).to receive(:resolution_proofer).and_return(instant_verify_proofer) - allow(instance).to receive(:lexisnexis_ddp_proofer).and_return(threatmetrix_proofer) - allow(instance).to receive(:state_id_proofer).and_return(aamva_proofer) + allow(progressive_proofer).to receive(:resolution_proofer).and_return(instant_verify_proofer) + allow(progressive_proofer).to receive(:lexisnexis_ddp_proofer).and_return(threatmetrix_proofer) + allow(progressive_proofer).to receive(:state_id_proofer).and_return(aamva_proofer) block_real_instant_verify_requests end @@ -104,7 +101,7 @@ def block_real_instant_verify_requests end subject(:proof) do - instance.proof( + progressive_proofer.proof( applicant_pii: applicant_pii, ipp_enrollment_in_progress: ipp_enrollment_in_progress, request_ip: Faker::Internet.ip_v4_address, @@ -209,7 +206,7 @@ def block_real_instant_verify_requests end it 'uses the transformed PII' do - allow(instance).to receive(:with_state_id_address).and_return(transformed_pii) + allow(progressive_proofer).to receive(:with_state_id_address).and_return(transformed_pii) expect(proof.same_address_as_id).to eq('true') expect(proof.ipp_enrollment_in_progress).to eq(true) @@ -230,12 +227,12 @@ def block_real_instant_verify_requests end it 'includes the state ID in the InstantVerify call' do - proof - - expect(instance).to have_received(:user_can_pass_after_state_id_check?). - with(instant_verify_proofer_result) - expect(instant_verify_proofer).to have_received(:proof). + expect(progressive_proofer).to receive(:user_can_pass_after_state_id_check?). + and_call_original + expect(instant_verify_proofer).to receive(:proof). with(hash_including(state_id_address)) + + proof end context 'the failure can be covered by AAMVA' do @@ -276,7 +273,7 @@ def block_real_instant_verify_requests end before do - allow(instance).to receive(:proof_residential_address_if_needed). + allow(progressive_proofer).to receive(:proof_residential_address_if_needed). and_return(residential_resolution_that_passed_instant_verify) end @@ -369,7 +366,7 @@ def block_real_instant_verify_requests let(:instant_verify_proofer_result) { residential_address_proof } before do - allow(instance).to receive(:proof_residential_address_if_needed). + allow(progressive_proofer).to receive(:proof_residential_address_if_needed). and_return(residential_address_proof) allow(residential_address_proof).to receive(:success?). and_return(false) diff --git a/yarn.lock b/yarn.lock index 70ab8487714..e01a0bc0fa3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4665,10 +4665,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libphonenumber-js@^1.11.2: - version "1.11.2" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.2.tgz#9ddd7d1a1e1be0e7c596c7e09487c362b4f1210c" - integrity sha512-V9mGLlaXN1WETzqQvSu6qf6XVAr3nFuJvWsHcuzCCCo6xUKawwSxOPTpan5CGOSKTn5w/bQuCZcLPJkyysgC3w== +libphonenumber-js@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.3.tgz#18126a2eec754eacd36f1f0d58590077fa5539ff" + integrity sha512-RU0CTsLCu2v6VEzdP+W6UU2n5+jEpMDRkGxUeBgsAJgre3vKgm17eApISH9OQY4G0jZYJVIc8qXmz6CJFueAFg== lightningcss-darwin-arm64@1.23.0: version "1.23.0"